2015-10-15 81 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