我們在了解shadowmap這項技術之前,先來了解一下什麼是陰影。在現實生活中,陰影伴隨著光而生。下面這張圖演示了在平行光下,影子是如何形成的。
圖中的兩個物體,在光照方向上,距離光源最近的那些點會被照亮,之後的點則不會接受到光照。所以判斷乙個點是否在陰影中的關鍵就是,它是否是光照方向上距離光源最近的點。
之前我們說過深度緩衝區,他可以用來判斷畫素是否需要被寫入顏色緩衝區。它的原理如下。
初始化所有的深度緩衝區資料為1。
系統在每個畫素寫入顏色緩衝區之前都會比較它的深度值和當前深度緩衝區對應的深度值,如果小於當前深度緩衝區對應的深度值,就寫入該畫素,並將當前深度緩衝區對應的深度值改為該畫素的深度值。否則,丟棄該畫素。
所有的深度值在比較前都會被規範化成0~1,投影矩陣近平面上的深度為0,遠平面上的深度為1。最終保留下來的都是深度最小的畫素點。我們可以利用這個特性來記錄光照方向上距離光源最近的點。
我們如果使用紋理來充當深度緩衝區,那麼我們將得到深度紋理。深度紋理是我們找到光照方向上距離光源最近的點的好幫手。假設我們在光源出放置乙個攝像機,觀察方向和光源方向調整為一致。然後使用正交投影矩陣進行投影。接著使用這個攝像機渲染場景。那麼深度紋理中就會記錄一系列距離光源最近的畫素點的深度值。
我們有了深度紋理,那麼如何使用它呢?在使用它之前我們需要明白畫素點的座標和深度紋理uv的對應關係。當我們使用光源的vp矩陣和模型矩陣變換畫素點的原始座標之後,座標值便落在了-1到1之間。我們把座標加上1再乘以0.5,xy就可以當做uv使用。使用xy在深度紋理上取值,就可以得到這個畫素點所對應的距離光源最近的點的深度值。將這個深度值與前面經過處理的畫素座標的z軸值比較,就可以判斷該畫素點是否在陰影中。
為了配合shadow map,fragment shader需要做以下改變。
新增深度紋理和光源處的vp矩陣。
uniform mat4 lightmatrix;
uniform sampler2d shadowmap;
複製**
獲取深度紋理上對應的深度值並和當前深度值做判斷,如果當前深度比距離光源最近的點深度大,則在陰影中,否則不在。經過處理的畫素座標的z軸可以作為當前深度值。它的範圍在0到1之間。
float shadow = 0.0;
vec4 positioninlightspace = lightmatrix * modelmatrix * vec4(fragposition, 1.0);
positioninlightspace /= positioninlightspace.w;
positioninlightspace = (positioninlightspace + 1.0) * 0.5;
vec2 shadowuv = positioninlightspace.xy;
if (shadowuv.x >= 0.0 && shadowuv.x <=1.0 && shadowuv.y >= 0.0 && shadowuv.y <=1.0) else
}複製**
為漫反射顏色加入陰影係數shadow的影響,shadow為1時,無影響,值越小,顏色越暗。
vec3 diffuse = diffusestrength * light.color * texture2d(diffusemap, fraguv).rgb * light.indensity * shadow;
複製**
想要生成光源處vp矩陣下的深度紋理,需要以下操作。
生成帶有深度紋理的framebuffer。
- (void)createshadowmap
glbindframebuffer(gl_framebuffer, 0);
}複製**
將gl_depth_component
深度格式的紋理貼圖作為framebuffer的深度緩衝區附件,所有渲染到深度緩衝區的資料都會被寫入shadowdepthmap
。
使用光源的vp渲染場景到上面的framebuffer。因為我們只需要深度資料,所以可以新建乙個fragment shader來渲染場景。乙個空的fragment shader
就足夠了,因為深度會預設寫入深度緩衝區,不需要任何處理。
precision highp float;
void main(void)
複製**
然後使用光源的vp渲染。lightprojectionmatrix
和lightcameramatrix
分別是光源的投影和觀察矩陣。到此我們就得到了光源空間的深度紋理了。
- (void)glkview:(glkview *)view drawinrect:(cgrect)rect
- (void)drawobjectsforshadowmap ];
}複製**
接下來我們來繪製場景到螢幕。
- (void)glkview:(glkview *)view drawinrect:(cgrect)rect
複製**
在[self drawobjects];
中,我們將深度紋理傳入fragment shader,而且還要傳入光源的vp矩陣。
- (void)drawobjects ];
}複製**
Android學習筆記高階15之Shader渲染
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!android提供的shader類主要是渲染影象以及一些幾何圖形。shader有幾個直接子類 bitmapshader 主要用來渲染影象 lineargradient 用來進行線性渲染 radialgradient 用來進行環形渲染 sweepgr...
Android學習筆記高階15之Shader渲染
android提供的shader類主要是渲染影象以及一些幾何圖形。shader有幾個直接子類 bitmapshader 主要用來渲染影象 lineargradient 用來進行線性渲染 radialgradient 用來進行環形渲染 sweepgradient 掃瞄漸變 圍繞乙個中心點掃瞄漸變就像電...
學習OpenGL ES之繪製地形
地形模型一般是由nxn的網格構成,網格的點在y軸上的座標由灰度地形圖上相應的顏色決定。顏色越亮,高度越高。顏色每個通道的取值範圍可以是0 255,通過公式轉換,可以很容易的控制生成模型的高度。上篇文章中,我們使用三角帶生成圓柱體的中間部分。現在我們要用多個三角帶來生成地形。如何生成單個三角帶我就不贅...