Adding a directional light to a transforming cube in vanilla wegl

℡╲_俬逩灬. 提交于 2019-12-12 20:55:35

问题


How can I add a direction light to a transforming cube in webGL.

I know it requires normals and i've add them in the snippet (i've commented them out)

It will also require some math in the vertex shader. unfortunatly this code doesnt work when i uncomment.

attribute vec4 coords;
  uniform mat4 transformMatrix;
  attribute vec3 normal;
  attribute vec4 colors;
  uniform vec3 lightColor;
  uniform vec3 lightDirection;
  varying vec4 varyingColors;
  uniform mat4 perspectiveMatrix;
  void main(void) {
    vec3 norm = normalize(normal);
    vec3 ld = normalize(lightDirection);
    float dotProduct = max(dot(norm, ld), 0.0);
    vec3 vertexColor = lightColor * colors * dotProduct;
    varyingColors = vec4(vertexColor, 1);
    gl_Position = perspectiveMatrix * transformMatrix  * coords;
  }

var gl,
    shaderProgram,
    vertices,
    matrix = mat4.create(),
    vertexCount,
    indexCount,
    q = quat.create(),
    translate =[-3, 0, -10],
    scale = [1,1,1],
    pivot = [0,0,0];
    
    translate2 = [0, 0, -8],
    scale2 = [3,3,3],
    pivot2 = [1,1,1]


initGL();
createShaders();
createVertices();
createIndices();
draw();

function initGL() {
  var canvas = document.getElementById("canvas");
  gl = canvas.getContext("webgl");
  gl.enable(gl.DEPTH_TEST);
  gl.viewport(0, 0, canvas.width, canvas.height);
  gl.clearColor(1, 1, 1, 1);
}

function createShaders() {
  var vertexShader = getShader(gl, "shader-vs");
  var fragmentShader = getShader(gl, "shader-fs");

  shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);
  gl.useProgram(shaderProgram);
}

function createVertices() {
  vertices = [
    -1, -1, -1,     1, 0, 0, 1,     // 0
     1, -1, -1,     1, 1, 0, 1,     // 1
    -1,  1, -1,     0, 1, 1, 1,     // 2
     1,  1, -1,     0, 0, 1, 1,     // 3
    -1,  1,  1,     1, 0.5, 0, 1,   // 4
     1,  1,  1,     0.5, 1, 1, 1,   // 5
    -1, -1,  1,     1, 0, 0.5, 1,   // 6
     1, -1,  1,     0.5, 0, 1, 1,   // 7
  ];

  vertexCount = vertices.length / 7;

  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

  var coords = gl.getAttribLocation(shaderProgram, "coords");
  gl.vertexAttribPointer(coords, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 7, 0);
  gl.enableVertexAttribArray(coords); 


/**
var normals = [
    0, 0, 1,   0, 0, 1,    0, 0, 1,   0, 0, 1,
    0, 1, 0,   0, 1, 0,    0, 1, 0,   0, 1, 0,
    0, 0, -1,  0, 0, -1,   0, 0, -1,  0, 0, -1,
    0, -1, 0,  0, -1, 0,   0, -1, 0,  0, -1, 0,
    -1, 0, 0,  -1, 0, 0,   -1, 0, 0,  -1, 0, 0,
    1, 0, 0,   1, 0, 0,    1, 0, 0,   1, 0, 0
  ];
  
  var normalBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
  
  var normalLocation = gl.getAttribLocation(shaderProgram, "normal");
  gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(normalLocation);  
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  
  var lightColor = gl.getUniformLocation(shaderProgram, "lightColor");
  gl.uniform3f(lightColor, 1, 1, 1);
  
  var lightDirection = gl.getUniformLocation(shaderProgram, "lightDirection");
  gl.uniform3f(lightDirection, 0.5, 1, 0);
 */


  var colorsLocation = gl.getAttribLocation(shaderProgram, "colors");
  gl.vertexAttribPointer(colorsLocation, 4, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 7, Float32Array.BYTES_PER_ELEMENT * 3);

  gl.enableVertexAttribArray(colorsLocation);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  

  var perspectiveMatrix = mat4.create();
  mat4.perspective(perspectiveMatrix, 1, canvas.width / canvas.height, 0.1, 11);
  var perspectiveLoc = gl.getUniformLocation(shaderProgram, "perspectiveMatrix");
  gl.uniformMatrix4fv(perspectiveLoc, false, perspectiveMatrix);

}

function createIndices() {
  var indices = [
    0, 1, 2,   1, 2, 3,
    2, 3, 4,   3, 4, 5,
    4, 5, 6,   5, 6, 7,
    6, 7, 0,   7, 0, 1,
    0, 2, 6,   2, 6, 4,
    1, 3, 7,   3, 7, 5
  ];
  indexCount = indices.length;

  var indexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);


}

function draw(timeMs) {
  requestAnimationFrame(draw);

  let interval = timeMs / 3000
  let t = interval - Math.floor(interval); 

  let trans_t = vec3.lerp([], translate, translate2, t);
  let scale_t = vec3.lerp([], scale, scale2, t);
  let pivot_t = vec3.lerp([], pivot, pivot2, t);

  mat4.fromRotationTranslationScaleOrigin(matrix, q, trans_t, scale_t, pivot_t);

  var transformMatrix = gl.getUniformLocation(shaderProgram, "transformMatrix");
  gl.uniformMatrix4fv(transformMatrix, false, matrix);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_BYTE, 0);
}


  /*
   * https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context
   */
  function getShader(gl, id) {
    var shaderScript, theSource, currentChild, shader;

    shaderScript = document.getElementById(id);

    if (!shaderScript) {
      return null;
    }

    theSource = "";
    currentChild = shaderScript.firstChild;

    while (currentChild) {
      if (currentChild.nodeType == currentChild.TEXT_NODE) {
        theSource += currentChild.textContent;
      }

      currentChild = currentChild.nextSibling;
    }
    if (shaderScript.type == "x-shader/x-fragment") {
      shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
      shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
      // Unknown shader type
      return null;
    }
    gl.shaderSource(shader, theSource);

// Compile the shader program
    gl.compileShader(shader);

// See if it compiled successfully
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
      return null;
    }

    return shader;
  }
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec4 coords;
attribute float pointSize;
uniform mat4 transformMatrix;
attribute vec4 colors;
varying vec4 varyingColors;
uniform mat4 perspectiveMatrix;
void main(void) {
  gl_Position = perspectiveMatrix * transformMatrix  * coords;
  gl_PointSize = pointSize;
  varyingColors = colors;
}
</script>

<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 color;
varying vec4 varyingColors;
void main(void) {
  gl_FragColor = varyingColors;
}
</script>

<canvas id="canvas" width="600" height="600"></canvas>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>

回答1:


There is an issue in your shader code:

vec3 vertexColor = lightColor * colors * dotProduct;

The type of color is vec4, but the type of vertexColor is vec3, so it has to be:

vec3 vertexColor = lightColor * colors.rgb * dotProduct;

You can't assigne a variable of type vec4 to a variable of type vec3.


When gl.vertexAttribPointer is called, then the proper vertex buffer has to be bound before. At the point where you specify the colorsLocation attribute, the buffer with the normals is bound, rather than than the buffer with the vertices and colors (vertices).


A vertex coordinate and its attributes are tuples. If a vertex coordinate is used twice (or 3 times for in three sides) and each time it is associated to a different normal vector, then the coordinate has to be "duplicated". Each vertex coordinate is associated to exactly 1 normal vector.
See Rendering meshes with multiple indices.

Imagine a 3 dimensional vertex coordinate and a 3 dimensional normal vector as 1, but 6 dimensional attribute.

Create a linearised array of attributes:

vertices = [
  [-1, -1, -1,     1, 0, 0, 1],     // 0
  [ 1, -1, -1,     1, 1, 0, 1],     // 1
  [-1,  1, -1,     0, 1, 1, 1],     // 2
  [ 1,  1, -1,     0, 0, 1, 1],     // 3
  [-1,  1,  1,     1, 0.5, 0, 1],   // 4
  [1,  1,  1,      0.5, 1, 1, 1],   // 5
  [-1, -1,  1,     1, 0, 0.5, 1],   // 6
  [1, -1,  1,      0.5, 0, 1, 1],   // 7
];

var normals = [
  [0, 0, 1], [0, 1, 0], [0, 0, -1],
  [0, -1, 0], [-1, 0, 0], [1, 0, 0] ];

var indices = [
  [0, 1, 2,   1, 2, 3],
  [2, 3, 4,   3, 4, 5],
  [4, 5, 6,   5, 6, 7],
  [6, 7, 0,   7, 0, 1],
  [0, 2, 6,   2, 6, 4],
  [1, 3, 7,   3, 7, 5]
];

var attributes = []
for(let side=0; side < indices.length; ++side) {
    for(let vi=0; vi < indices[side].length; ++vi) {
        attributes.push(...vertices[indices[side][vi]]);
        attributes.push(...normals[side]);
    }
}

vertexCount = attributes.length / 10;

Create the buffer and define the generic arrays of vertex attributes:

var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(attributes), gl.STATIC_DRAW);

var coords = gl.getAttribLocation(shaderProgram, "coords");
gl.vertexAttribPointer(coords, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, 0);
gl.enableVertexAttribArray(coords); 

var colorsLocation = gl.getAttribLocation(shaderProgram, "colors");
gl.vertexAttribPointer(colorsLocation, 4, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, Float32Array.BYTES_PER_ELEMENT * 3);
gl.enableVertexAttribArray(colorsLocation);

var normalLocation = gl.getAttribLocation(shaderProgram, "normal");
gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, Float32Array.BYTES_PER_ELEMENT * 7);
gl.enableVertexAttribArray(normalLocation);  

Use .drawArrays rather than .drawElements to draw the mesh:

//gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_BYTE, 0);
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);

See the example, where I applied the suggestions to your code:

var gl,
    shaderProgram,
    vertices,
    matrix = mat4.create(),
    vertexCount,
    indexCount,
    q = quat.create(),
    translate =[-3, 0, -10],
    scale = [1,1,1],
    pivot = [0,0,0];
    
    translate2 = [0, 0, -8],
    scale2 = [3,3,3],
    pivot2 = [1,1,1]


initGL();
createShaders();
createVertices();
draw();

function initGL() {
  var canvas = document.getElementById("canvas");
  gl = canvas.getContext("webgl");
  gl.enable(gl.DEPTH_TEST);
  gl.viewport(0, 0, canvas.width, canvas.height);
  gl.clearColor(1, 1, 1, 1);
}

function createShaders() {
  var vertexShader = getShader(gl, "shader-vs");
  var fragmentShader = getShader(gl, "shader-fs");

  shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);
  gl.useProgram(shaderProgram);
}

function createVertices() {
vertices = [
  [-1, -1, -1,     1, 0, 0, 1],     // 0
  [ 1, -1, -1,     1, 1, 0, 1],     // 1
  [-1,  1, -1,     0, 1, 1, 1],     // 2
  [ 1,  1, -1,     0, 0, 1, 1],     // 3
  [-1,  1,  1,     1, 0.5, 0, 1],   // 4
  [1,  1,  1,      0.5, 1, 1, 1],   // 5
  [-1, -1,  1,     1, 0, 0.5, 1],   // 6
  [1, -1,  1,      0.5, 0, 1, 1],   // 7
];

var normals = [
  [0, 0, 1], [0, 1, 0], [0, 0, -1],
  [0, -1, 0], [-1, 0, 0], [1, 0, 0] ];

var indices = [
  [0, 1, 2,   1, 2, 3],
  [2, 3, 4,   3, 4, 5],
  [4, 5, 6,   5, 6, 7],
  [6, 7, 0,   7, 0, 1],
  [0, 2, 6,   2, 6, 4],
  [1, 3, 7,   3, 7, 5]
];

var attributes = []
for(let side=0; side < indices.length; ++side) {
    for(let vi=0; vi < indices[side].length; ++vi) {
        attributes.push(...vertices[indices[side][vi]]);
        attributes.push(...normals[side]);
    }
}

  vertexCount = attributes.length / 10;

  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(attributes), gl.STATIC_DRAW);

  var coords = gl.getAttribLocation(shaderProgram, "coords");
  gl.vertexAttribPointer(coords, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, 0);
  gl.enableVertexAttribArray(coords); 

  var colorsLocation = gl.getAttribLocation(shaderProgram, "colors");
  gl.vertexAttribPointer(colorsLocation, 4, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, Float32Array.BYTES_PER_ELEMENT * 3);
  gl.enableVertexAttribArray(colorsLocation);

  var normalLocation = gl.getAttribLocation(shaderProgram, "normal");
  gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, Float32Array.BYTES_PER_ELEMENT * 7);
  gl.enableVertexAttribArray(normalLocation);  
  
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  
  var lightColor = gl.getUniformLocation(shaderProgram, "lightColor");
  gl.uniform3f(lightColor, 1, 1, 1);
  
  var lightDirection = gl.getUniformLocation(shaderProgram, "lightDirection");
  gl.uniform3f(lightDirection, 0.5, 0.5, -1);


  var perspectiveMatrix = mat4.create();
  mat4.perspective(perspectiveMatrix, 1, canvas.width / canvas.height, 0.1, 11);
  var perspectiveLoc = gl.getUniformLocation(shaderProgram, "perspectiveMatrix");
  gl.uniformMatrix4fv(perspectiveLoc, false, perspectiveMatrix);

}

function draw(timeMs) {
  requestAnimationFrame(draw);

  let interval = timeMs / 3000
  let t = interval - Math.floor(interval); 

  let trans_t = vec3.lerp([], translate, translate2, t);
  let scale_t = vec3.lerp([], scale, scale2, t);
  let pivot_t = vec3.lerp([], pivot, pivot2, t);

  mat4.fromRotationTranslationScaleOrigin(matrix, q, trans_t, scale_t, pivot_t);

  var transformMatrix = gl.getUniformLocation(shaderProgram, "transformMatrix");
  gl.uniformMatrix4fv(transformMatrix, false, matrix);
  gl.clear(gl.COLOR_BUFFER_BIT);

  //gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_BYTE, 0);
  gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
}


  /*
   * https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context
   */
  function getShader(gl, id) {
    var shaderScript, theSource, currentChild, shader;

    shaderScript = document.getElementById(id);

    if (!shaderScript) {
      return null;
    }

    theSource = "";
    currentChild = shaderScript.firstChild;

    while (currentChild) {
      if (currentChild.nodeType == currentChild.TEXT_NODE) {
        theSource += currentChild.textContent;
      }

      currentChild = currentChild.nextSibling;
    }
    if (shaderScript.type == "x-shader/x-fragment") {
      shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
      shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
      // Unknown shader type
      return null;
    }
    gl.shaderSource(shader, theSource);

// Compile the shader program
    gl.compileShader(shader);

// See if it compiled successfully
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
      return null;
    }

    return shader;
  }
<canvas id="canvas" width="600" height="600"></canvas>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec4 coords;
uniform mat4 transformMatrix;
attribute vec3 normal;
attribute vec4 colors;
uniform vec3 lightColor;
uniform vec3 lightDirection;
varying vec4 varyingColors;
uniform mat4 perspectiveMatrix;
void main(void) {
    vec3 norm = normalize(normal);
    vec3 ld = normalize(lightDirection);
    float dotProduct = max(dot(norm, ld), 0.0);
    vec3 vertexColor = lightColor * colors.rgb * dotProduct;
    varyingColors = vec4(vertexColor, 1);
    gl_Position = perspectiveMatrix * transformMatrix  * coords;
}
</script>

<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 color;
varying vec4 varyingColors;
void main(void) {
  gl_FragColor = varyingColors;
}
</script>


来源:https://stackoverflow.com/questions/55912327/adding-a-directional-light-to-a-transforming-cube-in-vanilla-wegl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!