This series mainly references the book "Unity Shaders and Effects Cookbook" (thanks to the author of the original book) and adds a bit of personal understanding or expansion.
Here are all the illustrations in this book. Here is the code and resources required for this book (you can also download them from the official website ).
========================================================== ===================================================== =====
Preface
Cloth is another very common coloring requirement. It is required in many real-time games for a more real interaction experience. It involves how to make the fabric fiber properly scatter the light on the entire surface and make it look like a fabric. The rendering of the fabric is very dependent on the changes in the Perspective. Therefore, we will learn some new techniques to simulate the effect of light scanning on the fabric, in addition, small fibers can produce distinctive edge lighting effects.
This article introduces two new concepts: Detail normal maps and Detail textures ). By combining these two normal textures, we can get a higher level of detail and store them in a 2048*2048 texture. This technology can help us simulate the uneven feeling of a very subtle layer on the surface, so as to disperse the high-gloss reflection of the entire surface.
The final effect of the fabric shader is shown below:
Preparations
This Shader needs to combine three different types of textures to simulate the fabric effect:
- A Detail Normal map ). This texture will be tiled on the surface to simulate small sewing traces.
- A standard Variation map ). This texture will simulate the changes in the sewing to prevent all surfaces from looking the same, but more like years of worn out.
- A Detail Diffuse map ). We use this texture to multiply the basic color to simulate the overall color of the fabric, so as to add more depth details and realism as a whole, and emphasize the sewing marks of the fabric.
The three textures required in this section are shown below. You can find them in the book resources (see the top.
At the same time, you also need to create a new scene, a parallel light, and an object (This section uses the built-in cloth model) as before ). Create a new Shader and Material and name it ClothShader.
Implementation
- First, add new properties. This is mainly used to control all textures, fresh, and high-gloss reflections.
Properties{_MainTint ("Global Tint", Color) = (1,1,1,1)_BumpMap ("Normal Map", 2D) = "bump" {}_DetailBump ("Detail Normal Map", 2D) = "bump" {}_DetailTex ("Fabric Weave", 2D) = "white" {}_FresnelColor ("Fresnel Color", Color) = (1,1,1,1)_FresnelPower ("Fresnel Power", Range(0, 12)) = 3_RimPower ("Rim FallOff", Range(0, 12)) = 3_SpecIntesity ("Specular Intensiity", Range(0, 1)) = 0.2_SpecWidth ("Specular Width", Range(0, 1)) = 0.2}
Explanation: Fresh reflection, in simple terms, is that when you observe the plane vertically, the reflection is very weak; but when the line of sight and the plane are smaller, the reflection is more obvious. For example, when you stand by the water side and observe the water surface, the water is transparent and the reflection is weak. But when you are farther away from the water surface, you can hardly see the part below the river surface, and the reflection is very strong. (Baidu encyclopedia)
- To fully control the effect of illumination on the cloth plane, we need to declare the new illumination model in the # pragma statement and set to use the Shader model 3.0.
CGPROGRAM#pragma surface surf Velvet#pragma target 3.0
- Now, we need to establish the connection between the Properties block and the SubShader block. To use various data in Properties, we need to declare variables with the same name in SubShader.
sampler2D _BumpMap;sampler2D _DetailBump;sampler2D _DetailTex;float4 _MainTint;float4 _FresnelColor;float _FresnelPower;float _RimPower;float _SpecIntesity;float _SpecWidth;
- To control the tile rates of several texture details, we need to declare their UV parameters in the Input structure. If you put uv in front of the same Paster name, you can establish the contact of UV information.
struct Input {float2 uv_BumpMap;float2 uv_DetailBump;float2 uv_DetailTex;};
- Now we need to create our lighting model function. First, you need to create the illumination function structure. We need the viewDir parameter to get the angle of view, because the surface of the cloth is affected by the angle of view.
inline fixed4 LightingVelvet (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten){}
- Always process all your illumination vectors from the beginning (here, the angle direction and illumination direction vectors, and their derivative vectors ). In this way, you do not need to always standardize your vectors, or worry about other aspects of illumination computing. Therefore, add the illumination vector at the beginning of the illumination model function:
//Create lighting vectors hereviewDir = normalize(viewDir);lightDir = normalize(lightDir);half3 halfVec = normalize (lightDir + viewDir);fixed NdotL = max (0, dot (s.Normal, lightDir));
Explanation:As you can see, halfVec combines lightDir and viewDir to compute the two vectors. For example, the highlight reflection here (the highlight reflection is related to the observation angle and the illumination angle ). NdotL is the component of the light in the normal direction of the plane. It is generally used to multiply the light color to obtain the color intensity of the actual light in the scenario.
- Next, we need to calculate the Specular part. Add the following code:
//Create Specular float NdotH = max (0, dot (s.Normal, halfVec));float spec = pow (NdotH, s.Specular*128.0) * s.Gloss;
Cloth rendering depends largely on the perspective from which you observe the plane. The more skewed the viewing angle, the more fibers capture the light behind the light and enhance the high-light reflection. (Fresh effect)
//Create Fresnelfloat HdotV = pow(1-max(0, dot(halfVec, viewDir)), _FresnelPower);float NdotE = pow(1-max(0, dot(s.Normal, viewDir)), _RimPower);float finalSpecMask = NdotE * HdotV
- After most calculations, we only need to output the final color value. Add the following code to complete the illumination model:
//Output the final colorfixed4 c;c.rgb = (s.Albedo * NdotL * _LightColor0.rgb) + (spec * (finalSpecMask * _FresnelColor)) * (atten * 2);c.a = 1.0;return c;
- Finally, we create the surf () function to complete our Shader. Here, we only need to extract the normal map and pass all the data to our SurfaceOutput structure.
void surf (Input IN, inout SurfaceOutput o) {half4 c = tex2D (_DetailTex, IN.uv_DetailTex);fixed3 normals = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)).rgb;fixed3 detailNormals = UnpackNormal(tex2D(_DetailBump, IN.uv_DetailBump)).rgb;fixed3 finalNormals = float3(normals.x + detailNormals.x, normals.y + detailNormals.y, normals.z + detailNormals.z);o.Normal = normalize(finalNormals);o.Specular = _SpecWidth;o.Gloss = _SpecIntesity;o.Albedo = c.rgb * _MainTint;o.Alpha = c.a;}
Explanation: In our fabric pasters, the new technology we demonstrate is how to integrate two normal maps with different tile rates. The basic linear algebra shows that we can add two vectors to get a new position. Therefore, we can operate our normal map in this way. We use the UnpackNormal () function to obtain the Normal vector of the standard Variation map, and then add it to the Normal vector of the Detail Normal map. In this way, a new normal map is obtained. Then, we standardize the final vector to make it range between 0 and 1. If this is not done, our normal map will look wrong.
The overall code is as follows:
Shader "Custom/ClothShader" {Properties{_MainTint ("Global Tint", Color) = (1,1,1,1)_BumpMap ("Normal Map", 2D) = "bump" {}_DetailBump ("Detail Normal Map", 2D) = "bump" {}_DetailTex ("Fabric Weave", 2D) = "white" {}_FresnelColor ("Fresnel Color", Color) = (1,1,1,1)_FresnelPower ("Fresnel Power", Range(0, 12)) = 3_RimPower ("Rim FallOff", Range(0, 12)) = 3_SpecIntesity ("Specular Intensiity", Range(0, 1)) = 0.2_SpecWidth ("Specular Width", Range(0, 1)) = 0.2}SubShader {Tags { "RenderType"="Opaque" }LOD 200CGPROGRAM#pragma surface surf Velvet#pragma target 3.0sampler2D _BumpMap;sampler2D _DetailBump;sampler2D _DetailTex;float4 _MainTint;float4 _FresnelColor;float _FresnelPower;float _RimPower;float _SpecIntesity;float _SpecWidth;struct Input {float2 uv_BumpMap;float2 uv_DetailBump;float2 uv_DetailTex;};inline fixed4 LightingVelvet (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten){//Create lighting vectors hereviewDir = normalize(viewDir);lightDir = normalize(lightDir);half3 halfVec = normalize (lightDir + viewDir);fixed NdotL = max (0, dot (s.Normal, lightDir));//Create Specular float NdotH = max (0, dot (s.Normal, halfVec));float spec = pow (NdotH, s.Specular*128.0) * s.Gloss;//Create Fresnelfloat HdotV = pow(1-max(0, dot(halfVec, viewDir)), _FresnelPower);float NdotE = pow(1-max(0, dot(s.Normal, viewDir)), _RimPower);float finalSpecMask = NdotE * HdotV;//Output the final colorfixed4 c;c.rgb = (s.Albedo * NdotL * _LightColor0.rgb) + (spec * (finalSpecMask * _FresnelColor)) * (atten * 2);c.a = 1.0;return c;}void surf (Input IN, inout SurfaceOutput o) {half4 c = tex2D (_DetailTex, IN.uv_DetailTex);fixed3 normals = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)).rgb;fixed3 detailNormals = UnpackNormal(tex2D(_DetailBump, IN.uv_DetailBump)).rgb;fixed3 finalNormals = float3(normals.x + detailNormals.x, normals.y + detailNormals.y, normals.z + detailNormals.z);o.Normal = normalize(finalNormals);o.Specular = _SpecWidth;o.Gloss = _SpecIntesity;o.Albedo = c.rgb * _MainTint;o.Alpha = c.a;}ENDCG} FallBack "Diffuse"}
The following shows the effects of our fabric coloring tool:
Explanation
In fact, our Shader is not complicated. It is nothing more than some basic illumination calculations, but sometimes these calculations are enough. When you want to use a Shader to simulate a certain surface, divide it into several parts, and then integrate them together at a certain time. The most important part is how you integrate different parts, just like mixing different layers in Photoshop.
Finally, we integrate the calculation of fresh and high-gloss reflection, this allows us to create the visual effects of tiny fibers that can also reflect light (here I understand that when it is tilted, we can see that the surface of the fabric is rough, the more detailed the fibers are ).
Conclusion
I feel that the original article is simple to explain, but it still seems a little difficult. In particular, the final color Assignment calculation in the illumination model feels that many calculations are actually integrated by experience and vision.
Call... I will write it here today. I hope you can learn more about it.