学习通过使用Unity Shader实现基础纹理的渲染效果
目录
问1:详细描述一下漫反射纹理、高度纹理、法线纹理、渐变纹理和遮罩纹理?
问2:详细说明一下什么是模型空间,什么是切线空间,什么是世界空间?
问3:详细说明一下法线纹理中存储法线分别在模型空间和切线空间的优缺点?
问4:详细纹理属性中的Wrap Mode属性的两中模式Repeat和Clamp的区别?
实验1:在Unity中使用单张纹理来代替物体的漫反射颜色,编写shader程序代码来实现这样的效果。
实验2:在Unity中使用漫反射纹理和法线纹理来实现物体的凸凹效果,注意在切线空间下进行光照计算,编写shader程序代码来实现这样的效果。
实验3:在Unity中使用漫反射纹理和法线纹理来实现物体的凸凹效果,注意在世界空间下进行光照计算,编写shader程序代码来实现这样的效果。
问1:详细描述一下漫反射纹理、高度纹理、法线纹理、渐变纹理和遮罩纹理?
答1:
漫反射纹理:通常使用一张纹理来代替物体的漫反射颜色。声明一个纹理变量,再使用tex2D函数采样。
高度纹理:用一张高度图来实现凹凸映射,高度图中存储的是强度值(intensity),用于表示模型表面局部的海拔高度。颜色越浅表明该位置的表面越向外凸起,颜色越深表明该位置越向里凹。
优点:非常直观,一眼看出模型表面的凹凸形状;
缺点:计算更加复杂,实时计算时不能直接得到表面法线,而需要像素的灰度值计算而得,消耗更多性能。
法线纹理:存储的是表面的法线方向。由于法线方向的分量范围在[-1,1],而像素的分量范围为[0,1],因此需要做一个映射,通常使用的映射是:
pixel = (normal+1)/2
要求我们在shader中对法线纹理进行纹理采样后,还需要对结果进行一次反映射的过程,以得到原先的法线方向。反映射的过程实际上就是使用上面映射函数的逆函数:
Normal = pixel x 2 – 1
模型顶点自带的法线,是定义在模型空间中的,将修改后的模型空间中的表面法线存储在一张纹理中,这种纹理被称为是模型空间的法线纹理(object-space normal map)。然而在实际制作当中,我们往往采用另一种坐标空间,即模型顶点的切线空间来存储法线。
渐变纹理:主要用于控制漫反射光照的结果,现多用于卡通风格的渲染。不同的渐变纹理有不同的特性。
遮罩纹理(mask texture):常见于很多商业游戏。遮罩是允许我们保护某些区域,使他们免于某些修改;或者制作地形材质时需要混合多张图片,例如草地的纹理,表现石子的纹理,表现裸露土地的纹理等。
流程:通过采样得到遮罩纹理的纹素值,使用其中某个(或某几个)通道的值来与某种表面属性进行相乘,这样,当该通道的值为0时,可以保护表面不受该属性的影响。
问2:详细说明一下什么是模型空间,什么是切线空间,什么是世界空间?
答2:
模型空间:所有法线所在的坐标空间是同一个坐标空。每个点存储的法线方向是各异的,经过映射之后存储到纹理中就对应了各种颜色。
切线空间:法线纹理看起来几乎全部是浅蓝色的,因为每个法线方向所在的坐标空间不一样,即是表面每点各自的切线空间。这种法线纹理存储了每个点在各自的切线空间中的法线扰动方向。
世界空间:定义的世界标准空间坐标系。
问3:详细说明一下法线纹理中存储法线分别在模型空间和切线空间的优缺点?
答3:
存储在模型空间的优点:
①实现简单,更加直观;
②在纹理坐标的缝合处和尖锐的边角部分,可见的突变(缝隙)较少,即可以提供的平滑的边界;
存储在切线空间的优点:
①自由度很高,切线空间下的法线纹理记录的是相对法线信息,即便把该纹理应用到一个完全不同的网格上,也可以得到一个合理的结果;
②可进行UV动画,通过移动一个纹理的UV坐标来实现一个凹凸移动的效果;
③可以重用法线纹理,一张法线纹理可以用到6个面的砖块;
④可压缩,每个法线纹理中法线的Z方向总是正方向,因此可以仅存储XY方向,而推导得到Z方向。
问4:详细纹理属性中的Wrap Mode属性的两中模式Repeat和Clamp的区别?
答4:
Repeat:使用这种模式,纹理将会不断重复;
Clamp:这种模式下超过范围的部分将会截取到边界值,形成一个条形结构。
实操
实验1:在Unity中使用单张纹理来代替物体的漫反射颜色,编写shader程序代码来实现这样的效果。
Shader "Unity Shaders Book/Chapter 7/Single Texture" {
//2* 为了使用纹理,我们需要在Priperties语义块中添加一个纹理属性:
Properties{
_Color("Color Tint", Color) = (1, 1, 1, 1)
_MainTex("Main Tex", 2D) = "white" {}
_Specular("Specular", Color) = (1, 1, 1, 1)
_Gloss("Gloss", Range(8.0, 256)) = 20
}
//3* 然后在SubShader语义块中定义了一个Pass语义块。而且,在Pass的第一行指明了该Pass的光照模式:
SubShader{
Pass {
Tags { "LightMode" = "ForwardBase" }
//4* 使用CGPROGRAM和END来包围住CG代码片,以定义最重要的顶点着色器和片元着色器代码。首先,我们使用#
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// Or just call the built-in function
// o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
// Use the texture to sample the diffuse color
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
实验2:在Unity中使用漫反射纹理和法线纹理来实现物体的凸凹效果,注意在切线空间下进行光照计算,编写shader程序代码来实现这样的效果。
Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BumpScale ("Bump Scale", Float) = 1.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
float3 lightDir: TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
//Compute the binormal
// float3 binormal = cross(normalize(v.normal),normalize(v.tangent.xyz))*v.tangent.w;
// //Construct a matrix which transform vectors from object space to tangent space
// float3×3 rotation = float3×3(v.tangent.xyz,binormal,v.normal);
//Or just use the bulid-in macro
TANGENT_SPACE_ROTATION;
//Transform the light direction from object space to tangent space
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
//Transform the view direction from object space to tangent space
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
// Get the texel in the normal map
fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
fixed3 tangentNormal;
// If the texture is not marked as "Normal map"
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
// Or mark the texture as "Normal map", and use the built-in funciton
tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
实验3:在Unity中使用漫反射纹理和法线纹理来实现物体的凸凹效果,注意在世界空间下进行光照计算,编写shader程序代码来实现这样的效果。
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BumpScale ("Bump Scale", Float) = 1.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
// Upgrade NOTE: excluded shader from DX11; has structs without semantics (struct v2f members pos,uv,TtoW0,TtoW1,TtoW2)
#pragma exclude_renderers d3d11
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float4 TtoW0:TEXCOORD1;
float4 TtoW1:TEXCOORD2;
float4 TtoW2:TEXCOORD3;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = v.texcoord.xy * _Bump_ST + _BumpMap_ST.zw;
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
//Compute the matrix that transform directions from tangent space to world space
//put the world position in w component for optimization
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}
fixed4 frag(v2f i) : SV_Target {
//Get the position in world space
float3 worldPos = float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
//Compute the light and vie Dir in world space
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldpos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//Get the normal in tangent space
fixed3 bump = UnpackNormal(tex2D(_BumpMap,i.uv.zw));
bump.xy *= _BumpScale;
bump.z = sqrt(1.0 - saturate(dot(bump.xy,bump.xy)));
//Transform the normal from the tangent space to world space
fixed3 normal = normalize(half3(dot(i.TtoW0.xyz,bump),dot(i.TtoW1.xyz,bump),dot(i.TtoW2.xyz,bump)));
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(normal, lightDir));
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(normal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
制作不易,来个一键三连鼓励下博主叭!!