最近一直在研究如何在個人 iPad 2
(只支持 OpenGL ES 2.0
, 不支持 3.0
) 上實現 視差貼圖
(Parallax Mapping
) 和 位移貼圖
(Displacement Mapping
).php
通過一番研究, 搜索閱讀了很多文章, 終於肯定, OpenGL ES 2.0
能夠支持 視差貼圖
, 暫時沒什麼好辦法支持 位移貼圖
.html
由於就我目前所瞭解的位移貼圖
, 有這麼兩種方法來實現, 一種是用 Tessellation
來提供多面數網格, 另外一種是在頂點着色器中對高度圖進行紋理採樣來計算對應的頂點偏移量.git
第一種方法就沒必要想了, 由於目前移動設備的 OpenGL ES 2.0/3.0
都不支持(貌似 DX11
和 OpenGL 4.0
才支持), 而 OpenGL ES 3.0
衍生自 OpenGL 3.3
.github
第二種方法目前看起來只能在 OpenGL ES 3.0
上使用, 請參考這篇文檔Jim's GameDev Blog: 置換貼圖 Displacement Mapping. 不過沒辦法在 OpenGL ES 2.0
上使用, 由於它要求在頂點着色器中進行紋理採樣, 而這個特性偏偏是 2.0
不支持, 3.0
支持的.app
咱們能夠看看 Jim
在 3.0
設備上實現位移貼圖
的效果:ide
原始圖:.net
使用位移貼圖
後的效果:3d
好了, 如今在咱們的 2.0
設備上實現咱們的 視差貼圖
吧, 先看看效果:code
使用不一樣參數的效果:orm
你能夠靈活調整這些參數:
看看這個視頻 video:
<embed src="http://player.youku.com/player.php/sid/XMTY3NTQ1MDc0OA==/v.swf" allowFullScreen="true" quality="high" width="480" height="400" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash"></embed>
代碼以下:
function setup() displayMode(OVERLAY) print("試驗 OpenGL ES 2.0 中位移貼圖的例子") print("Test the Parallax Mapping in OpenGL ES 2.0") img1 = readImage("Dropbox:dm") img2 = readImage("Dropbox:dnm1") img3 = readImage("Dropbox:dm1") local w,h = WIDTH,HEIGHT local c = color(223, 218, 218, 255) m3 = mesh() m3i = m3:addRect(w/2,h/2,w/1,h/1) m3:setColors(c) m3.texture = img1 m3:setRectTex(m3i,0,0,1,1) m3.shader = shader(shaders.vs,shaders.fs) m3.shader.diffuseMap = img1 m3.shader.normalMap = img2 m3.shader.depthMap = img3 -- local tb = m3:buffer("tangent") -- tb:resize(6) tchx,tchy = 0,0 end function draw() background(40, 40, 50) perspective() -- camera(e.x, e.y, e.z, p.x, p.y, p.z) -- 用於立方體 -- camera(300,300,600, 0,500,0, 0,0,1) -- 用於平面位移貼圖 camera(WIDTH/2,HEIGHT/2,1000, WIDTH/2,HEIGHT/2,-200, 0,1,0) -- mySp:Sphere(100,100,100,0,0,0,10) light = vec3(tchx, tchy, 100.75) view = vec3(tchx, tchy, 300.75) -- light = vec3(300, 300, 500) -- rotate(ElapsedTime*5,0,1,0) -- m3:setRect(m2i,tchx,tchy,WIDTH/100,HEIGHT/100) setShaderParam(m3) m3:draw() end function touched(touch) if touch.state == BEGAN or touch.state == MOVING then tchx=touch.x+10 tchy=touch.y+10 end end function setShaderParam(m) m.shader.model = modelMatrix() m.shader.lightPos = light -- m.shader.lightPos = vec3(0.5, 1.0, 900.3) m.shader.viewPos = vec3(WIDTH/2,HEIGHT/2,5000) m.shader.viewPos = view -- m.shader.viewPos = vec3(0.0, 0.0, 90.0) m.shader.parallax = true m.shader.height_scale = -0.015 end -- 試驗 視差貼圖 中的例子 shaders = { vs = [[ attribute vec4 position; attribute vec3 normal; attribute vec2 texCoord; //attribute vec3 tangent; //attribute vec3 bitangent; varying vec3 vFragPos; varying vec2 vTexCoords; varying vec3 vTangentLightPos; varying vec3 vTangentViewPos; varying vec3 vTangentFragPos; uniform mat4 projection; uniform mat4 view; uniform mat4 model; uniform mat4 modelViewProjection; uniform vec3 lightPos; uniform vec3 viewPos; void main() { //gl_Position = projection * view * model * position; gl_Position = modelViewProjection * position; vFragPos = vec3(model * position); vTexCoords = texCoord; // 根據法線 N 計算 T,B vec3 tangent; vec3 binormal; // 使用頂點屬性法線,並歸一化 vec3 Normal = normalize(normal*2.0-1.0); vec3 c1 = cross(Normal, vec3(0.0, 0.0, 1.0)); vec3 c2 = cross(Normal, vec3(0.0, 1.0, 0.0)); if ( length(c1) > length(c2) ) { tangent = c1; } else { tangent = c2;} // 歸一化切線和次法線 tangent = normalize(tangent); binormal = normalize(cross(normal, tangent)); vec3 T = normalize(mat3(model) * tangent); vec3 B = normalize(mat3(model) * binormal); vec3 N = normalize(mat3(model) * normal); mat3 TBN = mat3(T, B, N); vTangentLightPos = lightPos*TBN; vTangentViewPos = viewPos*TBN; vTangentFragPos = vFragPos*TBN; } ]], fs = [[ precision highp float; varying vec3 vFragPos; varying vec2 vTexCoords; varying vec3 vTangentLightPos; varying vec3 vTangentViewPos; varying vec3 vTangentFragPos; uniform sampler2D diffuseMap; uniform sampler2D normalMap; uniform sampler2D depthMap; uniform bool parallax; uniform float height_scale; vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir); vec2 ParallaxMapping1(vec2 texCoords, vec3 viewDir); // The Parallax Mapping vec2 ParallaxMapping1(vec2 texCoords, vec3 viewDir) { float height = texture2D(depthMap, texCoords).r; return texCoords - viewDir.xy / viewDir.z * (height * height_scale); } // Parallax Occlusion Mapping vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) { // number of depth layers const float minLayers = 10.0; const float maxLayers = 50.0; float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir))); // calculate the size of each layer float layerDepth = 1.0 / numLayers; // depth of current layer float currentLayerDepth = 0.0; // the amount to shift the texture coordinates per layer (from vector P) vec2 P = viewDir.xy / viewDir.z * height_scale; vec2 deltaTexCoords = P / numLayers; // get initial values vec2 currentTexCoords = texCoords; float currentDepthMapValue = texture2D(depthMap, currentTexCoords).r; while(currentLayerDepth < currentDepthMapValue) { // shift texture coordinates along direction of P currentTexCoords -= deltaTexCoords; // get depthmap value at current texture coordinates currentDepthMapValue = texture2D(depthMap, currentTexCoords).r; // get depth of next layer currentLayerDepth += layerDepth; } // -- parallax occlusion mapping interpolation from here on // get texture coordinates before collision (reverse operations) vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get depth after and before collision for linear interpolation float afterDepth = currentDepthMapValue - currentLayerDepth; float beforeDepth = texture2D(depthMap, prevTexCoords).r - currentLayerDepth + layerDepth; // interpolation of texture coordinates float weight = afterDepth / (afterDepth - beforeDepth); vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); return finalTexCoords; } void main() { // Offset texture coordinates with Parallax Mapping vec3 viewDir = normalize(vTangentViewPos - vTangentFragPos); vec2 texCoords = vTexCoords; if(parallax) texCoords = ParallaxMapping(vTexCoords, viewDir); // discards a fragment when sampling outside default texture region (fixes border artifacts) if(texCoords.x > 1.0 || texCoords.y > 1.0 || texCoords.x < 0.0 || texCoords.y < 0.0) discard; // Obtain normal from normal map vec3 normal = texture2D(normalMap, texCoords).rgb; normal = normalize(normal * 2.0 - 1.0); // Get diffuse color vec3 color = texture2D(diffuseMap, texCoords).rgb; // Ambient vec3 ambient = 0.1 * color; // Diffuse vec3 lightDir = normalize(vTangentLightPos - vTangentFragPos); float diff = max(dot(lightDir, normal), 0.0); vec3 diffuse = diff * color; // Specular vec3 reflectDir = reflect(-lightDir, normal); vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); vec3 specular = vec3(0.2) * spec; gl_FragColor = vec4(ambient + diffuse + specular, 1.0); } ]] }
其中法線圖
(img2),高度圖
(img3) 都是經過軟件 CrazyBump 根據原始紋理(img1)生成的.
你也能夠下載它們直接使用:
img1:
img2:
img3:
38 視差貼圖
視差貼圖(Parallax Mapping)與陡峭視差貼圖(Steep Palallax Mapping)
Parallax Occlusion Mapping in GLSL
Jim's GameDev Blog: 置換貼圖 Displacement Mapping