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 反射模型的像素级光照,您应该阅读 “平滑镜面高光”部分。
- 关于双面表面的像素级光照,您应该阅读 “双面平滑表面”部分。