11.表面着色器输入结构体

11.表面着色器-结构体-输入结构体


11.1 知识点

表面着色器中的输入结构体指什么

我们上节课主要学习了编译指令
#pragma surface 表面函数名 光照模型 可选额外参数
其中在讲解表面函数名时提到,需要按规则声明函数。函数的参数有三种固定格式:

  • void 表面函数名(Input IN, inout SurfaceOutput o)
  • void 表面函数名(Input IN, inout SurfaceOutputStandard o)
  • void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)

而其中的这个 Input 结构体,就是我们的输入结构体。它是由我们自己声明的,是数据的来源,我们需要用到这些数据进行逻辑处理。

输入结构体中我们能声明哪些成员

Input 结构体虽然是需要我们自己声明的结构体,但只要在其中按照规定声明成员变量,便能获取到指定的表面属性。注意:

  • 如果在可选额外参数中自定义了顶点修改函数,该结构体还会是顶点修改函数的输出结构体
  • 除了规范好的参数,我们还可以自己添加自定义参数,比如在自定义顶点修改函数中进行赋值
  • 在顶点/片元着色器中,这些参数往往需要手动计算,而在表面着色器中,可以理解为 Unity 内部已经帮助我们计算好了,直接拿来用即可
struct Input
{
    // 1. uv 相关  
    // 规则:uv 纹理变量名,若存在次纹理坐标,也可以用 uv2 作为前缀  
    float2 uv_MainTex;
    float2 uv_BumpMap;
    
    // 2. 视角方向(可以用于处理边缘光照等)  
    float3 viewDir;
    
    // 3. 屏幕空间坐标(可以用于处理反射或屏幕特效等)  
    float4 screenPos;
    
    // 4. 世界空间下的位置  
    float3 worldPos;
    
    // 5. 世界空间下的反射方向  
    //    没有修改 o.Normal 时;如果修改了表面法线 o.Normal,在表面函数中需要使用 WorldReflectionVector(IN, o.Normal) 来得到世界空间下的反射方向  
    float3 worldRefl;
    
    // 6. 世界空间下的法线方向  
    //    没有修改 o.Normal 时;如果修改了表面法线 o.Normal,在表面函数中需要使用 WorldNormalVector(IN, o.Normal) 来得到世界空间下的法线方向  
    float3 worldNormal;
    
    // 7. 使用 COLOR 语义定义的 float4 变量,表示插值后的逐顶点颜色  
    float4 vertexColor : COLOR;
};

表面函数示例

以下是表面函数的三种固定格式之一的示例,实现了对输入结构体中数据的使用:

// 1. void 表面函数名(Input IN, inout SurfaceOutput o)
// 2. void 表面函数名(Input IN, inout SurfaceOutputStandard o)
// 3. void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
void surf (Input IN, inout SurfaceOutput o)
{
    // 我们在 Input 结构体中声明了 uv_MainTex,就可以直接使用  
    // 只要满足命名规则,如视角方向、世界空间下的位置等也可以直接使用  
    // Unity 已经帮我们转换好了
    fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
    o.Albedo = tex.rgb * _Color;
    o.Alpha = tex.a * _Color.a;
    
    // 乘以凹凸程度的系数
    float3 tangentNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
    tangentNormal.xy *= _BumpScale;
    tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
    o.Normal = tangentNormal;
}

11.2 知识点代码

Lesson11_Shader.shader

Shader "Custom/Lesson11_Shader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("BumpMap", 2D) = "white"{}
        _BumpScale("BumpScale", Range(0,1)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        //vertex:FunVertex finalcolor:FunFinalcolor 
        #pragma surface surf Lambert
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _BumpMap;
        float _BumpScale;//凹凸程度
        fixed4 _Color;

        struct Input
        {
            //1.uv相关
            //规则:uv纹理变量名 若存在次纹理坐标,也可以用uv2作为前缀
            float2 uv_MainTex;
            float2 uv_BumpMap;
            
            //2.视角方向(可以用于处理边缘光照等)
            float3 viewDir;
            
            //3.屏幕空间坐标(可以用于处理反射或屏幕特效等)
            float4 screenPos;
            
            //4.世界空间下的位置
            float3 worldPos;
            
            //5.世界空间下的反射方向,没有修改o.Normal时
            //如果修改了表面法线 o.Normal
            //在表面函数中,需要使用 WorldReflectionVector(IN, o.Normal) 来得到世界空间下的反射方向
            float3 worldRefl;
            
            //6.世界空间下的法线方向,没有修改o.Normal时
            //如果修改了表面法线 o.Normal
            //在表面函数中,需要使用 WorldNormalVector(IN, o.Normal) 来得到世界空间下的法线方向
            float3 worldNormal;
            
            //7.使用COLOR语义定义的float4变量,表示插值后的逐顶点颜色
            float4 vertexColor:COLOR;
        };

        //1. void 表面函数名(Input IN, inout SurfaceOutput o)
        //2. void 表面函数名(Input IN, inout SurfaceOutputStandard o)
        //3. void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
        void surf (Input IN, inout SurfaceOutput o)
        {
            // 我们在Input结构体中声明了uv_MainTex
            // 就可以.出来直接使用
            // 只要满足命名规则 像视角方向 世界空间下的位置 也可以直接使用
            // Unity已经帮我们转换好了
            fixed4 tex = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = tex.rgb * _Color;
            o.Alpha = tex.a * _Color.a;
            
            //乘以凹凸程度的系数
            float3 tangentNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            tangentNormal.xy *= _BumpScale;
            tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
            o.Normal = tangentNormal;
        }

        void FunVertex(inout appdata_full v)
        {
            //处理顶点逻辑
            
        }

        void FunFinalcolor(Input IN, SurfaceOutput o, inout fixed4 color)
        {
            //对最终处理完的颜色 再次进行额外处理
        }

        //不依赖视角的光照模型,比如漫反射
        //half4 Lighting自定义名字 (SurfaceOutput s, half3 lightDir, half atten)
        //依赖视角的光照模型,比如高光反射
        //half4 Lighting自定义名字 (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
        //然后只需要在光照模型处填写 自定义名字 即可
        //就会自动调用函数中的逻辑来处理光照相关逻辑了
        half4 LightingMyLight (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
        {

        }

        ENDCG
    }
    FallBack "Diffuse"
}

Lesson11_表面着色器_输入结构体.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson11_表面着色器_输入结构体 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 表面着色器中的输入结构体指什么

        //我们上节课主要学习了编译指令
        //#pragma surface 表面函数名 光照模型 可选额外参数
        //其中在讲解表面函数名时提到,需要按规则声明函数
        //函数的参数有三种固定格式
        //1. void 表面函数名(Input IN, inout SurfaceOutput o)
        //2. void 表面函数名(Input IN, inout SurfaceOutputStandard o)
        //3. void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
        //而其中的这个Input结构体,就是我们的输入结构体
        //它是由我们自己声明的,是数据的来源,我们需要用到这些数据进行逻辑处理

        #endregion

        #region 知识点二 输入结构体中我们能声明哪些成员

        //Input结构体虽然是需要我们自己声明的结构体
        //但是我们只要在其中按照规定声明成员变量,便能获取到指定的表面属性
        //注意:
        //1.如果我们在可选额外参数中自定义了顶点修改函数,该结构体还会是顶点修改函数的输出结构体
        //2.除了规范好的参数,我们还可以自己添加自定义参数,比如在自定义顶点修改函数中进行赋值
        //3.在顶点/片元着色器中,这些参数往往需要我们手动计算,而在表面着色器中你可以理解为Unity内部已经
        //  帮助我们计算好了,直接拿来用即可

        #endregion
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏