- 编程指南:shadowMap示例连接
- 编程指南部分代码

- 主要业务逻辑
- 获取灯光位置的深度值的贴图
- 把深度值贴图和灯光位置传入mesh中
- 灯光作为相机的投影空间,转到相对于相机的屏幕空间,来输入深度值,和获取深度值
- 根据灯光的位置获取所有顶点的深度值,如果改顶点位置大于深度值位置,颜色变暗
获取gl变量
var canvas = document.getElementById('webgl');
var gl = getWebGLContext(canvas);
var OFFSCREEN_WIDTH = 2048, OFFSCREEN_HEIGHT = 2048;
var LIGHT_X = 0, LIGHT_Y = 7, LIGHT_Z = 2;
var currentAngle = 0.0;
var mvpMatrixFromLight_t = new Matrix4();
var mvpMatrixFromLight_p = new Matrix4();
获取Plane、Triangle顶点缓冲区,颜色缓冲区,索引缓冲区
- createBuffer bindBuffer bindBufferData
var triangle = initVertexBuffersForTriangle(gl);
var plane = initVertexBuffersForPlane(gl);
function initVertexBuffersForTriangle(gl) {
var vertices = new Float32Array([-0.8, 3.5, 0.0, 0.8, 3.5, 0.0, 0.0, 3.5, 1.8]);
var colors = new Float32Array([1.0, 0.5, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 0.0]);
var indices = new Uint8Array([0, 1, 2]);
var o = new Object();
o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null;
o.numIndices = indices.length;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
return o;
}
function initVertexBuffersForPlane(gl) {
var vertices = new Float32Array([
3.0, -1.7, 2.5,
-3.0, -1.7, 2.5,
-3.0, -1.7, -2.5,
3.0, -1.7, -2.5
]);
var colors = new Float32Array([
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0
]);
var indices = new Uint8Array([0, 1, 2, 0, 2, 3]);
var o = new Object();
o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null;
o.numIndices = indices.length;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
return o;
}
function initArrayBufferForLaterUse(gl, data, num, type) {
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
buffer.num = num;
buffer.type = type;
return buffer;
}
function initElementArrayBufferForLaterUse(gl, data, type) {
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW);
buffer.type = type;
return buffer;
}
FBO
- 先获取fbo,后面用
- fbo还有一种方式绑定
- createTexture bindTexture texImage2D texParameteri
- createRenderbuffer bindRenderbuffer renderbufferStorage
- createFramebuffer bindFramebuffer framebufferTexture2D framebufferRenderbuffer
var fbo = initFramebufferObject(gl);
function initFramebufferObject(gl) {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
var depthBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);
var framebuffer = gl.createFramebuffer();
framebuffer.texture = texture;
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
return framebuffer;
}
使用深度shader和材质
- 深度材质,用在平面和三角面上的,然后给fbo获取为那个角度的深度值
var shadowProgram = createProgram(gl, SHADOW_VSHADER_SOURCE, SHADOW_FSHADER_SOURCE);
var SHADOW_VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
'}\n';
var SHADOW_FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'void main() {\n' +
' gl_FragColor = vec4(gl_FragCoord.z, 0.0, 0.0, 0.0);\n' +
'}\n';
shadowProgram.a_Position = gl.getAttribLocation(shadowProgram, 'a_Position');
shadowProgram.u_MvpMatrix = gl.getUniformLocation(shadowProgram, 'u_MvpMatrix');
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fbo.texture);
var viewProjMatrixFromLight = new Matrix4();
viewProjMatrixFromLight.setPerspective(70.0, OFFSCREEN_WIDTH/OFFSCREEN_HEIGHT, 1.0, 100.0);
viewProjMatrixFromLight.lookAt(LIGHT_X, LIGHT_Y, LIGHT_Z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
bindFramebuffer drawcall
- 相机位置就在灯光的位置 viewProjMatrixFromLight
- 所有物体都会进行drawcall
- 然后生成深度图,都是最表面的深度
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.viewport(0, 0, OFFSCREEN_HEIGHT, OFFSCREEN_HEIGHT);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(shadowProgram);
drawTriangle(gl, shadowProgram, triangle, currentAngle, viewProjMatrixFromLight);
drawPlane(gl, shadowProgram, plane, viewProjMatrixFromLight);
- 生成阴影材质,主要是用在平面上的

当前相机视角下的shader
var normalProgram = createProgram(gl, VSHADER_SOURCE, FSHADER_SOURCE);
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'uniform mat4 u_MvpMatrixFromLight;\n' +
'varying vec4 v_PositionFromLight;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
' v_PositionFromLight = u_MvpMatrixFromLight * a_Position;\n' +
' v_Color = a_Color;\n' +
'}\n';
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'uniform sampler2D u_ShadowMap;\n' +
'varying vec4 v_PositionFromLight;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n' +
' vec4 rgbaDepth = texture2D(u_ShadowMap, shadowCoord.xy);\n' +
' float visibility = (shadowCoord.z > rgbaDepth.r + 0.005) ? 0.7 : 1.0;\n' +
' gl_FragColor = vec4(v_Color.rgb * visibility, v_Color.a);\n' +
'}\n';
normalProgram.a_Position = gl.getAttribLocation(normalProgram, 'a_Position');
normalProgram.a_Color = gl.getAttribLocation(normalProgram, 'a_Color');
normalProgram.u_MvpMatrix = gl.getUniformLocation(normalProgram, 'u_MvpMatrix');
normalProgram.u_MvpMatrixFromLight = gl.getUniformLocation(normalProgram, 'u_MvpMatrixFromLight');
normalProgram.u_ShadowMap = gl.getUniformLocation(normalProgram, 'u_ShadowMap');
var viewProjMatrix = new Matrix4();
viewProjMatrix.setPerspective(45, canvas.width/canvas.height, 1.0, 100.0);
viewProjMatrix.lookAt(0.0, 9.0, 9.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(normalProgram);
gl.uniform1i(normalProgram.u_ShadowMap, 0);
drawTriangle(gl, normalProgram, triangle, currentAngle, viewProjMatrix);
gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_p.elements);
drawPlane(gl, normalProgram, plane, viewProjMatrix);
var g_modelMatrix = new Matrix4();
var g_mvpMatrix = new Matrix4();
function drawTriangle(gl, program, triangle, angle, viewProjMatrix) {
g_modelMatrix.setRotate(angle, 0, 1, 0);
draw(gl, program, triangle, viewProjMatrix);
}
function drawPlane(gl, program, plane, viewProjMatrix) {
g_modelMatrix.setRotate(-45, 0, 1, 1);
draw(gl, program, plane, viewProjMatrix);
}
function draw(gl, program, o, viewProjMatrix) {
initAttributeVariable(gl, program.a_Position, o.vertexBuffer);
if (program.a_Color != undefined)
initAttributeVariable(gl, program.a_Color, o.colorBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, o.indexBuffer);
g_mvpMatrix.set(viewProjMatrix);
g_mvpMatrix.multiply(g_modelMatrix);
gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);
gl.drawElements(gl.TRIANGLES, o.numIndices, gl.UNSIGNED_BYTE, 0);
}
function initAttributeVariable(gl, a_attribute, buffer) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0);
gl.enableVertexAttribArray(a_attribute);
}