2015-10-15 87 views
1

我想第一次在WebGL场景中实现阴影,据我所知,最直接的方法是使用阴影贴图,但我找不到解释这个概念的教程,这不是关于OpenGL,就是包含像three.js这样的库。如何在WebGL中实现阴影映射?

但是,我读过,我必须写两对着色器。第一对着色器用于构建阴影贴图,该贴图必须存储在帧缓冲区对象中。然后,我将不得不从帧缓冲区中取出存储的阴影贴图,并将其传递给第二对着色器,用于绘制场景中的对象。

阴影贴图必须包含信息,在光线照射到物体之前光源可以从光源传播的距离,并将该信息传递给用于绘制物体的着色器,这将有可能指定哪些部件通过来自光源的光点亮,哪些部件仅通过环境光照亮。

好这么多的理论,但我不知道该怎样得到这个工作的想法...

对于实验,我已经建立了一个非常简单的场景,呈现由单一的点光源照明两个领域源,它看起来像这样:

spheres lit by point light

两个领域在[0.0, 0.0, 0.0]和半径1.0它们的中心创建的。

较大球体的矩阵由[-2.0, 0.0, -2.0]翻译并由[2.0, 2.0, 2.0]缩放。

较小球体的矩阵由[2.0, 0.0, 2.0]翻译并且由[0.5, 0.5, 0.5]缩放。

点光源的位置是[4.0, 0.0, 4.0]

因此,较小的球体现在只是在较大的球体和光源之间,因此在较大的球体表面上应该存在一个不直接点亮的区域。

两个着色器我用这个场景是这样的:

顶点着色器

attribute vec4 aPosition; 
    attribute vec3 aNormal; 

    uniform mat4 uProjectionMatrix; 
    uniform mat4 uModelViewMatrix; 
    uniform mat3 uNormalMatrix; 

    varying vec4 vPosition; 
    varying vec3 vTransformedNormal; 

    void main () { 
    vTransformedNormal = uNormalMatrix * aNormal; 
    vPosition = uModelViewMatrix * aPosition; 
    gl_Position = uProjectionMatrix * vPosition; 
    } 

片段着色器

precision highp float; 

    uniform vec3 uLightPosition; 

    varying vec4 vPosition; 
    varying vec3 vTransformedNormal; 

    void main () { 
    vec3 lightDirection = normalize(uLightPosition - vPosition.xyz); 

    float diffuseLightWeighting = max(
     dot(normalize(vTransformedNormal), lightDirection), 0.0); 

    vec3 lightWeighting = vec3(0.1, 0.1, 0.1) + 
     vec3(0.8, 0.8, 0.8) * diffuseLightWeighting; 

    gl_FragColor = vec4(vec3(1.0, 1.0, 1.0) * lightWeighting, 1.0); 
    } 

所以,第一件事我会现在必须做的是编写另一对着色器,并且因为它们不习惯真正的博士在某种程度上,我可以忽略法线的属性以及法线矩阵的均匀性,也不需要投影矩阵,对吗?

第二个顶点着色器可以再看看这样的:

attribute vec4 aPosition; 

    uniform mat4 uModelViewMatrix; 

    varying vec4 vPosition; 

    void main () { 
    vPosition = uModelViewMatrix * aPosition; 
    gl_Position = vPosition; 
    } 
有关片段着色器

可是什么?而且,无论如何,我如何才能找出点光源发出的光线照射到物体的哪个位置?我的意思是,一般需要从全部相关对象一次传递顶点位置数据,不是吗? (虽然在这种特殊情况下,只需要通过较小球体的顶点位置......)

现在,我的问题是,我怎么从这里继续?必须写入片段着色器,如何保存阴影贴图,以及如何使用它来计算更大球体上较小球体的阴影?

我担心我可能需求太多,但如果有人能够至少让我指向正确的方向,那将是非常好的。

回答

2

参见http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/

基本上,你发现,阴影映射与2次进行。第一遍写入/渲染到光源的视角的深度纹理,然后在第二遍中使用该纹理来确定像素是否处于阴影中。这个想法基本上是,如果像素比阴影贴图的深度/距离更远,那么这意味着场景包含另一个更接近光线/遮挡光线的物体。 AKA,当前像素在阴影中。

第一遍着色器基本上是一个标准顶点着色器,其中有一个空的片段着色器,用于渲染深度纹理。深度纹理是目前支持很好的扩展。

在第二遍中,您需要设置一些方法来进行上述深度比较。然后,基本上,您可以根据深度比较的结果计算阴影值,通常为1或0,并在照明计算中使用该值。例如,1的阴影值可以散射光线和镜面光线。

至于如何更改渲染输出目的地,您需要使用FrameBuffer对象。

完整的安装可能是这个样子:

// HANDLE VBOS 

fbo_shadow.setAsDrawTarget(); 
gl.clear(gl.DEPTH_BUFFER_BIT); 

shadow_depth_shader.use(); 
shadow_depth.setUnif("u_viewProjection", lightMatrix); 

// DRAW EVERYTHING 

fbo_lightPass.setAsDrawTarget(); 
gl.clear(gl.COLOR_BUFFER_BIT); 

lighting_pass_shader.use(); 
lighting_pass_shader.setUnif("u_shadowMap", fbo_shadow.depthTexture); 
lighting_pass_shader.setUnif("u_viewProjection", camera.getVPMatrix()); 
// other unifs ... 

// DRAW EVERYTHING AGAIN 

编辑:增加了对片段着色器的样本shadowCalculation功能:

float shadowCalculation(vec4 fragPosLightSpace, sampler2D u_shadowMap, float bias){ 
    // perform perspective divide and map to [0,1] range 
    vec3 projCoords = fragPosLightSpace.xyz/fragPosLightSpace.w; 
    projCoords = projCoords * 0.5 + 0.5; 
    float shadowDepth = texture2D(u_shadowMap, projCoords.xy).r; 
    float depth = projCoords.z; 
    float shadow = step(depth-bias,shadowDepth); 
    return shadow; 
} 
+0

你好Wacław并非常感谢您的回答!那么,如果我找到了你的话,我首先要做的第一步就是创建一个'lightMatrix',它应该是一个mat4。因此,在第一遍的顶点着色器中,它看起来像'vPosition = lightMatrix * modelViewMatrix * aPosition',或者我也必须考虑projectionMatrix,比如'vPosition = uProjectionMatrix * lightMatrix * modelViewMatrix * aPosition'?为了获得第二遍vec3,我必须通过添加'1'作为第四个值来使用像glMatrix'vec4.transformMat4'这样的东西,对吧? – SickBoy

+0

我也很难找到关于深度纹理的东西:MSDN根本没有描述这个扩展,而MDN只有一个死链接。基本语法应该是'depthTexture = gl.getExtension('WEBGL_depth_texture')',对吧?你有什么链接可以看到哪些浏览器支持这个功能? _caniuse.com_对WebGL没有用处,上面提到的网络也没有。我记得我已经阅读过有关将值存储为常规纹理的RGBA值的内容,因此可以用作后备?看起来,learnwebgl.com已经离线了...... – SickBoy

+0

......这非常令人伤心,因为如果我没有记错的话,还有关于如何使用frameBuffer对象来制作可用于第二遍。我从来没有使用过这种技术,因此我对它的工作原理只有一些模糊的想法 - 现在我找不到解释这一点的文章或教程。但是,对我来说,这是一个很大的问题:如果你想学习纯WebGL,很难找到任何文章,文档和教程!这就像试图学习JavaScript一样,你在网上找到的所有东西都只是关于jQuery或Java。 :-( – SickBoy