GLSL 程式設計/Unity/半透明表面

本教程涵蓋半透明表面。
它是關於光照的多個教程之一,超越了 Phong 反射模型。然而,它基於畫素級光照,使用 Phong 反射模型,如 “平滑鏡面高光”部分 中所述。如果您尚未閱讀本教程,您應該先閱讀它。
Phong 反射模型沒有考慮半透明性,即光線穿過材料的可能性。本教程是關於半透明表面,即允許光線從一個面傳送到另一個面的表面,例如紙張、衣服、塑膠薄膜或樹葉。

我們將區分兩種光線傳輸:漫射半透明和前向散射半透明,它們分別對應於 Phong 反射模型中的漫射項和鏡面項。漫射半透明類似於 Phong 反射模型中的漫射反射項(參見 “漫射反射”部分)對光線進行漫射傳輸:它只取決於表面法線向量和光源方向的點積 - 除了我們使用負表面法線向量,因為光源在背面,因此漫射半透明照明的方程為
這是許多半透明表面最常見的照明,例如紙張和樹葉。
一些半透明表面(例如塑膠薄膜)幾乎是透明的,允許光線幾乎直接地穿過表面,但會有一些前向散射;也就是說,人們可以透過表面看到光源,但影像會有點模糊。這類似於 Phong 反射模型的鏡面項(參見 “鏡面高光”部分 的方程),除了我們用負光線方向 -L 代替反射光線方向 R,指數 現在對應於前向散射光的銳利度
當然,這種前向散射半透明模型並不準確,但它允許我們偽造效果並調整引數。
以下實現基於 “平滑鏡面高光”部分,它展示了使用 Phong 反射模型的畫素級光照。該實現允許渲染背面,並在此情況下翻轉表面法線向量。一個更詳細的版本還可以使用不同的顏色表示正面和背面(參見 “雙面平滑表面”部分)。除了 Phong 反射模型的各項外,我們還計算漫射半透明和前向散射半透明的照明。以下是片段著色器特有的部分
#ifdef FRAGMENT
void main()
{
vec3 normalDirection = normalize(varyingNormalDirection);
if (!gl_FrontFacing) // do we look at the backface?
{
normalDirection = -normalDirection; // flip normal
}
vec3 viewDirection =
normalize(_WorldSpaceCameraPos - vec3(position));
vec3 lightDirection;
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
vec3 vertexToLightSource =
vec3(_WorldSpaceLightPos0 - position);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
// Computation of the Phong reflection model:
vec3 ambientLighting =
vec3(gl_LightModel.ambient) * vec3(_Color);
vec3 diffuseReflection =
attenuation * vec3(_LightColor0) * vec3(_Color)
* max(0.0, dot(normalDirection, lightDirection));
vec3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation * vec3(_LightColor0)
* vec3(_SpecColor) * pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}
// Computation of the translucent illumination:
vec3 diffuseTranslucency = attenuation * vec3(_LightColor0)
* vec3(_DiffuseTranslucentColor)
* max(0.0, dot(lightDirection, -normalDirection));
vec3 forwardTranslucency;
if (dot(normalDirection, lightDirection) > 0.0)
// light source on the wrong side?
{
forwardTranslucency = vec3(0.0, 0.0, 0.0);
// no forward-scattered translucency
}
else // light source on the right side
{
forwardTranslucency = attenuation * vec3(_LightColor0)
* vec3(_ForwardTranslucentColor) * pow(max(0.0,
dot(-lightDirection, viewDirection)), _Sharpness);
}
// Computation of the complete illumination:
gl_FragColor = vec4(ambientLighting
+ diffuseReflection + specularReflection
+ diffuseTranslucency + forwardTranslucency, 1.0);
}
#endif
完整的著色器程式碼定義了材料常量的著色器屬性,併為附加光源添加了另一個通道,使用加法混合,但沒有環境光
Shader "GLSL translucent surfaces" {
Properties {
_Color ("Diffuse Material Color", Color) = (1,1,1,1)
_SpecColor ("Specular Material Color", Color) = (1,1,1,1)
_Shininess ("Shininess", Float) = 10
_DiffuseTranslucentColor ("Diffuse Translucent Color", Color)
= (1,1,1,1)
_ForwardTranslucentColor ("Forward Translucent Color", Color)
= (1,1,1,1)
_Sharpness ("Sharpness", Float) = 10
}
SubShader {
Pass {
Tags { "LightMode" = "ForwardBase" }
// pass for ambient light and first light source
Cull Off // show frontfaces and backfaces
GLSLPROGRAM
// User-specified properties
uniform vec4 _Color;
uniform vec4 _SpecColor;
uniform float _Shininess;
uniform vec4 _DiffuseTranslucentColor;
uniform vec4 _ForwardTranslucentColor;
uniform float _Sharpness;
// The following built-in uniforms (except _LightColor0)
// are also defined in "UnityCG.glslinc",
// i.e. one could #include "UnityCG.glslinc"
uniform vec3 _WorldSpaceCameraPos;
// camera position in world space
uniform mat4 _Object2World; // model matrix
uniform mat4 _World2Object; // inverse model matrix
uniform vec4 _WorldSpaceLightPos0;
// direction to or position of light source
uniform vec4 _LightColor0;
// color of light source (from "Lighting.cginc")
varying vec4 position;
// position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;
// surface normal vector in world space
#ifdef VERTEX
void main()
{
mat4 modelMatrix = _Object2World;
mat4 modelMatrixInverse = _World2Object; // unity_Scale.w
// is unnecessary because we normalize vectors
position = modelMatrix * gl_Vertex;
varyingNormalDirection = normalize(vec3(
vec4(gl_Normal, 0.0) * modelMatrixInverse));
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif
#ifdef FRAGMENT
void main()
{
vec3 normalDirection = normalize(varyingNormalDirection);
if (!gl_FrontFacing) // do we look at the backface?
{
normalDirection = -normalDirection; // flip normal
}
vec3 viewDirection =
normalize(_WorldSpaceCameraPos - vec3(position));
vec3 lightDirection;
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
vec3 vertexToLightSource =
vec3(_WorldSpaceLightPos0 - position);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
vec3 ambientLighting =
vec3(gl_LightModel.ambient) * vec3(_Color);
vec3 diffuseReflection =
attenuation * vec3(_LightColor0) * vec3(_Color)
* max(0.0, dot(normalDirection, lightDirection));
vec3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation * vec3(_LightColor0)
* vec3(_SpecColor) * pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}
vec3 diffuseTranslucency = attenuation * vec3(_LightColor0)
* vec3(_DiffuseTranslucentColor)
* max(0.0, dot(lightDirection, -normalDirection));
vec3 forwardTranslucency;
if (dot(normalDirection, lightDirection) > 0.0)
// light source on the wrong side?
{
forwardTranslucency = vec3(0.0, 0.0, 0.0);
// no forward-scattered translucency
}
else // light source on the right side
{
forwardTranslucency = attenuation * vec3(_LightColor0)
* vec3(_ForwardTranslucentColor) * pow(max(0.0,
dot(-lightDirection, viewDirection)), _Sharpness);
}
gl_FragColor = vec4(ambientLighting
+ diffuseReflection + specularReflection
+ diffuseTranslucency + forwardTranslucency, 1.0);
}
#endif
ENDGLSL
}
Pass {
Tags { "LightMode" = "ForwardAdd" }
// pass for additional light sources
Cull Off
Blend One One // additive blending
GLSLPROGRAM
// User-specified properties
uniform vec4 _Color;
uniform vec4 _SpecColor;
uniform float _Shininess;
uniform vec4 _DiffuseTranslucentColor;
uniform vec4 _ForwardTranslucentColor;
uniform float _Sharpness;
// The following built-in uniforms (except _LightColor0)
// are also defined in "UnityCG.glslinc",
// i.e. one could #include "UnityCG.glslinc"
uniform vec3 _WorldSpaceCameraPos;
// camera position in world space
uniform mat4 _Object2World; // model matrix
uniform mat4 _World2Object; // inverse model matrix
uniform vec4 _WorldSpaceLightPos0;
// direction to or position of light source
uniform vec4 _LightColor0;
// color of light source (from "Lighting.cginc")
varying vec4 position;
// position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;
// surface normal vector in world space
#ifdef VERTEX
void main()
{
mat4 modelMatrix = _Object2World;
mat4 modelMatrixInverse = _World2Object; // unity_Scale.w
// is unnecessary because we normalize vectors
position = modelMatrix * gl_Vertex;
varyingNormalDirection = normalize(vec3(
vec4(gl_Normal, 0.0) * modelMatrixInverse));
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif
#ifdef FRAGMENT
void main()
{
vec3 normalDirection = normalize(varyingNormalDirection);
if (!gl_FrontFacing) // do we look at the backface?
{
normalDirection = -normalDirection; // flip normal
}
vec3 viewDirection =
normalize(_WorldSpaceCameraPos - vec3(position));
vec3 lightDirection;
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
vec3 vertexToLightSource =
vec3(_WorldSpaceLightPos0 - position);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
vec3 diffuseReflection =
attenuation * vec3(_LightColor0) * vec3(_Color)
* max(0.0, dot(normalDirection, lightDirection));
vec3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation * vec3(_LightColor0)
* vec3(_SpecColor) * pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}
vec3 diffuseTranslucency = attenuation * vec3(_LightColor0)
* vec3(_DiffuseTranslucentColor)
* max(0.0, dot(lightDirection, -normalDirection));
vec3 forwardTranslucency;
if (dot(normalDirection, lightDirection) > 0.0)
// light source on the wrong side?
{
forwardTranslucency = vec3(0.0, 0.0, 0.0);
// no forward-scattered translucency
}
else // light source on the right side
{
forwardTranslucency = attenuation * vec3(_LightColor0)
* vec3(_ForwardTranslucentColor) * pow(max(0.0,
dot(-lightDirection, viewDirection)), _Sharpness);
}
gl_FragColor = vec4(diffuseReflection + specularReflection
+ diffuseTranslucency + forwardTranslucency, 1.0);
}
#endif
ENDGLSL
}
}
// The definition of a fallback shader should be commented out
// during development:
// Fallback "Specular"
}
恭喜!您完成了本教程關於半透明表面的學習,它們非常常見,但無法用 Phong 反射模型來模擬。我們已經涵蓋了
- 什麼是半透明表面。
- 哪些形式的半透明性最常見(漫射半透明和前向散射半透明)。
- 如何實現漫射半透明和前向散射半透明。
如果您還想了解更多資訊
- 關於 Phong 反射模型的漫射項,您應該閱讀 “漫射反射”部分。
- 關於 Phong 反射模型的環境項或鏡面項,您應該閱讀 “鏡面高光”部分。
- 關於使用 Phong 反射模型的畫素級光照,您應該閱讀 “平滑鏡面高光”部分。
- 關於雙面表面的畫素級光照,您應該閱讀 “雙面平滑表面”部分。