首先,我想说,我读过很多关于使用深度地图和立方体地图进行阴影映射的文章,我了解它们是如何工作的,我也有使用OpenGL的工作经验,但是,我有一个问题,在我的三维图形引擎"EZ3“中使用一个单点光源实现全方位阴影映射技术。我的引擎使用WebGL作为三维图形API,JavaScript作为编程语言,这是为我的学士论文计算机科学。
基本上,这就是我实现阴影映射算法的方式,但我只关注点灯的情况,因为有了它们,我可以归档全方位的阴影映射。
首先,我积极地面对这样的选择:
if (this.state.faceCulling !== Material.FRONT) {
if (this.state.faceCulling === Material.NONE)
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);
this.state.faceCulling = Material.FRONT;
}其次,我创建了一个深度程序来记录每个cubemap面的深度值,这是我在GLSL1.0中的深度程序代码:
顶点着色器:
precision highp float;
attribute vec3 position;
uniform mat4 uModelView;
uniform mat4 uProjection;
void main() {
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}碎片着色器:
precision highp float;
vec4 packDepth(const in float depth) {
const vec4 bitShift = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
const vec4 bitMask = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
vec4 res = mod(depth * bitShift * vec4(255), vec4(256)) / vec4(255);
res -= res.xxyz * bitMask;
return res;
}
void main() {
gl_FragData[0] = packDepth(gl_FragCoord.z);
}第三,这是我的JavaScript函数“归档”全方位阴影映射的主体。
program.bind(gl);
for (i = 0; i < lights.length; i++) {
light = lights[i];
// Updates pointlight's projection matrix
light.updateProjection();
// Binds point light's depth framebuffer
light.depthFramebuffer.bind(gl);
// Updates point light's framebuffer in order to create it
// or if it's resolution changes, it'll be created again.
light.depthFramebuffer.update(gl);
// Sets viewport dimensions with depth framebuffer's dimensions
this.viewport(new Vector2(), light.depthFramebuffer.size);
if (light instanceof PointLight) {
up = new Vector3();
view = new Matrix4();
origin = new Vector3();
target = new Vector3();
for (j = 0; j < 6; j++) {
// Check in which cubemap's face we are ...
switch (j) {
case Cubemap.POSITIVE_X:
target.set(1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_X:
target.set(-1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.POSITIVE_Y:
target.set(0, 1, 0);
up.set(0, 0, 1);
break;
case Cubemap.NEGATIVE_Y:
target.set(0, -1, 0);
up.set(0, 0, -1);
break;
case Cubemap.POSITIVE_Z:
target.set(0, 0, 1);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_Z:
target.set(0, 0, -1);
up.set(0, -1, 0);
break;
}
// Creates a view matrix using target and up vectors according to each face of pointlight's
// cubemap. Furthermore, I translate it in minus light position in order to place
// the point light in the world's origin and render each cubemap's face at this
// point of view
view.lookAt(origin, target, up);
view.mul(new EZ3.Matrix4().translate(light.position.clone().negate()));
// Flips the Y-coordinate of each cubemap face
// scaling the projection matrix by (1, -1, 1).
// This is a perspective projection matrix which has:
// 90 degress of FOV.
// 1.0 of aspect ratio.
// Near clipping plane at 0.01.
// Far clipping plane at 2000.0.
projection = light.projection.clone();
projection.scale(new EZ3.Vector3(1, -1, 1));
// Attaches a cubemap face to current framebuffer in order to record depth values for the face with this line
// gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + j, id, 0);
light.depthFramebuffer.texture.attach(gl, j);
// Clears current framebuffer's color with these lines:
// gl.clearColor(1.0,1.0,1.0,1.0);
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
this.clear(color);
// Renders shadow caster meshes using the depth program
for (k = 0; k < shadowCasters.length; k++)
this._renderShadowCaster(shadowCasters[k], program, view, projection);
}
} else {
// Directional light & Spotlight case ...
}
}第四,使用我的主顶点着色器中的深度cubemap &片段着色器来计算全方位阴影映射:
顶点着色器:
precision highp float;
attribute vec3 position;
uniform mat4 uModel;
uniform mat4 uModelView;
uniform mat4 uProjection;
varying vec3 vPosition;
void main() {
vPosition = vec3(uModel * vec4(position, 1.0));
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}碎片着色器:
float unpackDepth(in vec4 color) {
return dot(color, vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 ));
}
float pointShadow(const in PointLight light, const in samplerCube shadowSampler) {
vec3 direction = vPosition - light.position;
float vertexDepth = clamp(length(direction), 0.0, 1.0);
float shadowMapDepth = unpackDepth(textureCube(shadowSampler, direction));
return (vertexDepth > shadowMapDepth) ? light.shadowDarkness : 1.0;
}最后,这是我得到的结果,我的场景有一个平面,一个立方体和一个球体。此外,红亮球是点光源:

正如你所看到的,我看起来就像点光深度帧缓冲区的cubemap -在他们的脸上做的不是很好的插值。
直到现在,我还不知道怎么解决这个问题。
发布于 2015-12-15 00:12:41
几天后,我意识到我在用一个以度为单位的视场角度计算投影矩阵,它应该是弧度。我做了转换,现在一切都很好。在我的深度帧缓冲区的立方体地图的脸之间的插值现在是完美的。因此,在弧度中处理每一个三角函数的角度是很重要的。
此外,我意识到,正如我在问题中所说的那样,您可以计算视图矩阵,并以这种方式计算:
view.lookAt(position, target.add(position.clone()), up);这种方法意味着你的观点被放置在点灯的中心,你只是呈现在你的立方体地图的每个方向,但是这些方向是哪一个呢?这些方向是计算出来的,把我在开关块中的每个目标(根据每个立方体地图的脸)与你的点灯位置相加。
此外,没有必要翻转投影矩阵的Y坐标,在这种情况下,可以将点对点的透视投影矩阵调到你的GLSL着色器,而不用缩放它(1,-1,1),因为我正在处理没有翻转Y坐标的纹理,我认为只有在处理翻转纹理的Y坐标时,才应该翻转点光投影矩阵的Y坐标,这样才能产生正确的全方位阴影映射效果。
最后,我将在这里留下我在CPU/GPU侧的全方位阴影映射算法的最终版本。在CPU方面,我将解释您必须做的每一步,以便为每个cubemap的脸计算一个正确的阴影映射。另一方面,在GPU方面,我将在我的主要片段着色器中解释我的深度程序的顶点/片段着色器和全方位阴影映射功能,这是为了帮助可能正在学习这项技术的人,或者解决今后对这个算法的怀疑:
// Disable blending and enable front face culling.
this.state.disable(gl.BLEND);
this.state.enable(gl.CULL_FACE);
this.state.cullFace(gl.FRONT);
// Binds depth program
program.bind(gl);
// For each pointlight source do
for (i = 0; i < lights.length; i++) {
light = lights[i];
// Get each pointlight's world position
position = light.worldPosition();
// Binds pointlight's depth framebuffer. Besides, in this function,
// viewport's dimensions are set according to depth framebuffer's dimension.
light.depthFramebuffer.bind(gl, this.state);
// Updates point light's framebuffer in order to create it
// or if it's resolution have changed, it'll be created again.
light.depthFramebuffer.update(gl);
// Check in which cubemap's face we are ...
for (j = 0; j < 6; j++) {
switch (j) {
case Cubemap.POSITIVE_X:
target.set(1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_X:
target.set(-1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.POSITIVE_Y:
target.set(0, 1, 0);
up.set(0, 0, 1);
break;
case Cubemap.NEGATIVE_Y:
target.set(0, -1, 0);
up.set(0, 0, -1);
break;
case Cubemap.POSITIVE_Z:
target.set(0, 0, 1);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_Z:
target.set(0, 0, -1);
up.set(0, -1, 0);
break;
}
// Creates a view matrix using target and up vectors
// according to each face of pointlight's cubemap.
view.lookAt(position, target.add(position.clone()), up);
// Attaches cubemap's face to current framebuffer
// in order to record depth values in that direction.
light.depthFramebuffer.texture.attach(gl, j);
// Clears color & depth buffers of your current framebuffer
this.clear();
// Render each shadow caster mesh using your depth program
for (k = 0; k < meshes.length; k++)
this._renderMeshDepth(program, meshes[k], view, light.projection);
}
}关于renderMeshDepth函数,我已经:
// Computes pointlight's model-view matrix
modelView.mul(view, mesh.world);
// Dispatch each matrix to the GLSL depth program
program.loadUniformMatrix(gl, 'uModelView', modelView);
program.loadUniformMatrix(gl, 'uProjection', projection);
// Renders a mesh using vertex buffer objects (VBO)
mesh.render(gl, program.attributes, this.state, this.extensions);深度程序顶点着色器:
precision highp float;
attribute vec3 position;
uniform mat4 uModelView;
uniform mat4 uProjection;
void main() {
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}深度计划片段阴影:
precision highp float;
// The pack function distributes fragment's depth precision storing
// it throughout (R,G,B,A) color channels and not just R color channel
// as usual in shadow mapping algorithms. This is because I'm working
// with 8-bit textures and one color channel hasn't enough precision
// to store a depth value.
vec4 pack(const in float depth) {
const vec4 bitShift = vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0);
const vec4 bitMask = vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
vec4 res = fract(depth * bitShift);
res -= res.xxyz * bitMask;
return res;
}
void main() {
// Packs normalized fragment's Z-Coordinate which is in [0,1] interval.
gl_FragColor = pack(gl_FragCoord.z);
}在我的主要片段着色器中的全方位阴影映射功能:
// Unpacks fragment's Z-Coordinate which was packed
// on the depth program's fragment shader.
float unpack(in vec4 color) {
const vec4 bitShift = vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
return dot(color, bitShift);
}
// Computes Omnidirectional Shadow Mapping technique using a samplerCube
// vec3 lightPosition is your pointlight's position in world coordinates.
// vec3 vPosition is your vertex's position in world coordinates, in code
// I mean this -> vPosition = vec3(uModel * vec4(position, 1.0));
// where uModel is your World/Model matrix.
float omnidirectionalShadow(in vec3 lightPosition, in float bias, in float darkness, in samplerCube sampler) {
vec3 direction = vPosition - lightPosition;
float vertexDepth = clamp(length(direction), 0.0, 1.0);
float shadowMapDepth = unpack(textureCube(sampler, direction)) + bias;
return (vertexDepth > shadowMapDepth) ? darkness : 1.0;
}这里有算法的最终呈现。

有乐趣的编码美丽的图形,祝好运:)
C.Z.
https://computergraphics.stackexchange.com/questions/1788
复制相似问题