Just My Life & My Work

原本想用WebGL寫,不過語法不太熟悉,還是選用GLSL來實做Displacement Mapping

語言:GLSL 模型:teapot 貼圖:color mapdisplacement map

  • 左為color map,右為displacement map

原理:

運算:P1 = P0 + (N * df * uf)

P0是原本頂點的位置、P1是經過displace後頂點的位置、N是model原本的法向量(displace後不變)、uf是自訂尺度、df = 0.30*dv.x + 0.59*dv.y + 0.11*dv.z,dv是displacement map的值,由R、G、B轉為灰階。

  • 使用displacement map後diffuse的效果。

  • 使用displacement map後specular的效果。

  • 使用displacement map後color map的效果。

  • 使用displacement map後color map * diffuse + specular的效果。

  • 「不」使用displacement map後color map * diffuse + specular的效果。

  • 使用displacement map後normal map的specular效果。

  • 使用displacement map後color map * diffuse + normal map的specular效果。

在這裡我多加Bump Mapping的效果,來彌補就算Displacement Mapping能改變vertex的位置,但卻無法改變normal的缺點(就算shader會做內插,但內插結果往往跟理想的不一樣)。此外,Displacement Mapping暴露了一個缺點,就是當model的vertex不多時,得到的效果相當有限。

到這裡,vertex和pixel都有運算來改變model的特性,做到相當真實的效果,若還想要再真實一點,還可以加入陰影,用AO(Ambient Occlusion) map來做。

vertex shader程式碼:


varying vec3 vertex_position;
varying vec3 vertex_light_vector;
varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;

uniform sampler2D displacement_texture;

void main() {
vec4 newVertexPos;
vec4 dv;
float df;

gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;

dv = texture2D( displacement_texture, gl_MultiTexCoord0.xy );

df = 0.30*dv.x + 0.59*dv.y + 0.11*dv.z;

newVertexPos = vec4(gl_Normal * df * 0.3, 0.0) + gl_Vertex;

gl_Position = gl_ModelViewProjectionMatrix * newVertexPos;

// Calculate the normal value for this vertex, in world coordinates (multiply by gl_NormalMatrix)
vertex_normal = normalize(gl_NormalMatrix * gl_Normal);
vertex_position = vec3(gl_ModelViewMatrix * newVertexPos);

// Calculate the light position for this vertex
vec3 light_position = gl_LightSource[0].position.xyz;
vertex_light_vector = normalize(light_position.xyz - vertex_position.xyz);

// Calculate the light's half vector
vertex_light_half_vector = normalize(gl_LightSource[0].halfVector.xyz);
}

fragment shader程式碼:


varying vec3 vertex_position;
varying vec3 vertex_light_vector;
varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;
const vec3 eye_position = vec3(0.0,0.0,0.0);

uniform float updown, leftright, farnear;

uniform sampler2D color_texture;
uniform sampler2D normal_texture;

void main() {

// Extract the normal from the normal map
vec3 normal = normalize(texture2D(normal_texture, gl_TexCoord[0].st).rgb * 2.0 - 1.0);

// Determine where the light is positioned
vec3 light_pos = normalize(vec3(leftright, updown, farnear) + vertex_light_vector);

// Calculate the lighting diffuse value
float diffuse = max(dot(normal, light_pos), 0.0);

vec3 E = normalize(vec3(eye_position - vertex_position));
//vec3 R_d = normalize(vec3(reflect(-light_pos, vertex_normal)));
vec3 R_n = normalize(vec3(reflect(-light_pos, normal)));

// Defining The Material Colors
const vec4 AmbientColor = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 DiffuseColor = vec4(0.5, 0.5, 0.5, 1.0);
const vec4 SpecularColor = vec4(1.0, 1.0, 1.0, 1.0);

// Calculate the ambient term
vec4 ambient_color = AmbientColor * gl_LightSource[0].ambient + gl_LightModel.ambient * gl_FrontMaterial.ambient;

// Calculate the diffuse term
vec4 diffuse_color = DiffuseColor * gl_LightSource[0].diffuse;
// Set the diffuse value (darkness). This is done with a dot product between the normal and the light
// and the maths behind it is explained in the maths section of the site.
float diffuse_value = max(dot(normalize(vertex_normal), vertex_light_vector), 0.0);

// Calculate the specular value
//vec4 specular_color_d = SpecularColor * gl_LightSource[0].specular * pow(max(dot(E, R_d), 0.0) , 100.0);
vec4 specular_color_n = SpecularColor * gl_LightSource[0].specular * pow(max(dot(E, R_n), 0.0) , 100.0);

vec3 color = diffuse_value * texture2D(color_texture, gl_TexCoord[0].st).rgb + specular_color_n;

// Set the output color of our current pixel
gl_FragColor = vec4(color, 1.0);
}

參考:Vertex Displacement Mapping using GLSLDisplacement Mapping

廣告

Comments on: "[GLSL] 位移映射 (Displacement Mapping)" (5)

  1. 逍遙文大您好,

    在看到了您對displacement mapping的描述後, 發現此方法可解決我目前的問題, 我現在正試著要透過WebGL將地球的表面造成有真實的高低起伏, 然後在顯示在網頁上, 而現在我本來就可以把png貼成一個解析度8192X4096的地球, 也有了灰階的地圖. 但問題就是如何實現, 我目前的想法是將原坐標加上調整後的值, 也就是: 調整後座標 = 法向量 X 灰階值 + 原座標; 我在這裡是否可以跟您請教, 相關知識的學習, 是否有推薦的外文書目或網站來學相關知識, 在這方面我實在不知道有什麼中文書有相關的, 還請賜教.

    • 嗨~你好!感謝你提出這麼詳細的問題~
      這篇文章是我一年半之前研究GLSL的成果,現在忘了差不多XD~所以能回答你的問題可能不會很詳細,當然未來若有這類問題還是可以發問啦~
      可以教你的是,GLSL的code可以直接用來寫WebGL,要注意的是「圖片座標」和「模型座標」的對應關係,應用上述的公式得出point新的位置,就能達到你要的效果。

  2. […] 接著想製作真正凹凸不平的太空隕石,原理與效果可以參考我這篇:位移映射 (Displacement Mapping),Blender教學:法向量貼圖與太空隕石。 […]

  3. Vertex太少,寫個Geometry Shader內插增加數目即可,與Vertex Displacement相甫是為Tessellation。

隨意留個言吧:)~

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

標籤雲

%d 位部落客按了讚: