• unity surface shader 1



    Unity ShaderLib :  CGPROGRAM  ENDCG之间是CG代码,之外的代码功能都由ShaderLib提供,CG中的一些方法比如tex2D(...)也是ShaderLib对CG进行了封装


    SurfaceShader全部函数Demo

    Shader "Nafio/BaseSurf" {
    
    Properties
    {
    	_MainTex("_MainTex",2D) = "white" {}
    	_Color("Color",Color) = (1,1,1,1)
    }
    
    SubShader
    {
    	Tags{"RenderType" = "Opaque"}
    	LOD 200
    	CGPROGRAM
    	#pragma surface surf CustomLambert vertex:vert finalcolor:final
    	sampler2D _MainTex;
    	fixed4 _Color;
    	struct Input{
    		half2 uv_MainTex;
    		float4 vertcolor;
    	};
    
    	void vert(inout appdata_full v,out Input IN)
    	{
    		UNITY_INITIALIZE_OUTPUT(Input,IN);//不初始化会报错..
    		IN.vertcolor = v.color;
    	}
    
    	void surf(Input IN,inout SurfaceOutput o)
    	{
    		half4 c = tex2D(_MainTex,IN.uv_MainTex) * _Color;
    		o.Albedo = c.rgb;
    		o.Alpha = c.a;
    	}
    
    	inline half4 LightingCustomLambert(SurfaceOutput s,half3 LightDir,half atten)
    	{
    		half nl = max(0,dot(s.Normal,LightDir));
    		half4 c;
    		c.rgb = s.Albedo * _LightColor0.rgb * (nl * atten * 2);
    		c.a = s.Albedo;
    		return c;
    	}
    
    	void final(Input IN,SurfaceOutput s,inout fixed4 c)
    	{
    		c = c * 0.9+0.1;
    	}
    
    	ENDCG
    }
    FallBack "Diffuse"
    
    }



    普通漫反射

    Shader "Nafio/NDiffuse" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}//变量名_MainText,显示名Base(RGB),类型2D,默认值白色white,最后{}里面可以放一些控制参数
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }   //处理不透明物体用这段shader
    		LOD 200    //LOD 200如果Unity中配置的Lod小于这个值,那么这段shader就不起作用,unity中maxLod默认配的0,就是所有数值都起作用
    		//另外可以用代码来制定具体shader的LOD限制
    		CGPROGRAM  //CG开始
    		#pragma surface surf Lambert  //surface使用surface shader   surf表面shader函数名     Lambert使用的光照模型为Lambert
    
    		sampler2D _MainTex;  //定义一张2d图片(就是从属性中的_MainTex链接过来的,否则cg无法使用)
    
    		struct Input {
    			float2 uv_MainTex //取MainTex的uv
    		};
    
    		void surf (Input IN, inout SurfaceOutput o) {   //IN是向表面着色器处理函数输入的函数,SurfaceOutput是向光照处理函数输出的参数
    			half4 c = tex2D (_MainTex, IN.uv_MainTex); //获取—_MainText固定uv点的颜色出来
    			o.Albedo = c.rgb;
    			o.Alpha = c.a;
    		}
    		ENDCG
    	} 
    	FallBack "Diffuse"//如果当前shader不能正确执行,回调默认shader
    }

    注意:这里surf函数执行的是执行光照前的操作



    使用surf处理法线贴图

    Shader "Custom/Normal Mapping" {  
        Properties {
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _Bump ("Bump", 2D) = "bump" {} //法线贴图
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 200
    
            CGPROGRAM
            #pragma surface surf Lambert
    
            sampler2D _MainTex;
            sampler2D _Bump;                
    
            struct Input {
                float2 uv_MainTex;
                float2 uv_Bump;
            };
    
            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex);
    
                o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump);   //获取法线信息
    
                o.Albedo = c.rgb;
                o.Alpha = c.a;
            }
            ENDCG
        } 
        FallBack "Diffuse"
    }
    




    加入自定义光照模型

    Shader "Nafio/NDiffuseLight" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf CustomDiffuse //CustomDiffuse自定义光照模型函数名
    
    		sampler2D _MainTex;
    
    		struct Input {
    			float2 uv_MainTex;
    		};
    
    		void surf (Input IN, inout SurfaceOutput o) {
    			half4 c = tex2D (_MainTex, IN.uv_MainTex);
    			o.Albedo = c.rgb;
    			o.Alpha = c.a;
    		}
    		
    		inline float4 LightingCustomDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten) {  //LightingCustomDiffuse名称与上面定义有关,s是surf函数的输出,lightDir光方向,atten衰减系数
        		float difLight = max(0, dot (s.Normal, lightDir));
        		float4 col;
        		col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
        		col.a = s.Alpha;
        		return col;
    		}
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    


    UV滚动

    Shader "Nafio/NScrollTexture" {
    	Properties {
    		_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_ScrollXSpeed ("XSpeed",Range(0,10)) = 2
    		_ScrollYSpeed ("YSpeed",Range(0,10)) = 2
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf Lambert
    
    		fixed4 _MainTint;//fixed为定点数,可以模拟浮点数,特点计算速度快
    		sampler2D _MainTex;
    		fixed _ScrollXSpeed;
    		fixed _ScrollYSpeed;
    		
    		struct Input {
    			float2 uv_MainTex;
    		};
    
    		void surf (Input IN, inout SurfaceOutput o) {
    		
    			fixed2 scrollUV = IN.uv_MainTex;
    			fixed xScrollValue = _ScrollXSpeed * _Time;//这里的shader中的time跟unity中的Time类似
    			fixed yScrollValue = _ScrollYSpeed * _Time;
    			scrollUV += fixed2(xScrollValue,yScrollValue);
    			
    			half4 c = tex2D (_MainTex, scrollUV);
    			o.Albedo = c.rgb * _MainTint;//_MainTint可有可无,不影响滚动,只是一个附加色
    			o.Alpha = c.a;
    		}
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    


    unity中默认的BlinnPhong镜面反射

    Shader "Nafio/NDefaultSpec" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_MainTint ("Diffuse Tint",Color) = (1,1,1,1)
    		_SpecColor ("Specular Color",Color) = (1,1,1,1)
    		_SpecPower("Specular Color",Range(0,1)) = 0.5
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf BlinnPhong
    
    		sampler2D _MainTex;
    		float _SpecPower;
    		float4 _MainTint;
    
    		struct Input {
    			float2 uv_MainTex;
    		};
    
    		void surf (Input IN, inout SurfaceOutput o) {
    			
    			half4 c = tex2D (_MainTex, IN.uv_MainTex)*_MainTint;
    			o.Specular = _SpecPower;
    			o.Gloss = 1.0;
    			o.Albedo = c.rgb;
    			o.Alpha = c.a;
    		}
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    


    unity中默认的Phong

    Shader "Nafio/NPhong" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_MainTint("Diffuse Tint",Color) = (1,1,1,1)
    		_SpecularColor("Specular Color",Color) = (1,1,1,1)
    		_SpecularPower("Specular Power",Range(0,30)) = 1
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf Phong
    
    		sampler2D _MainTex;
    		float4 _MainTint;//nafio 这些变量为何不用fixed:fixed范围-2 - 2
    		float4 _SpecularColor;
    		float _SpecularPower;
    
    		struct Input {
    			float2 uv_MainTex;//nafio 为何只有uv在这里传:这是input用来从vertex shader向fragram shader传递数据,需要什么定义什么
    		};
    
    		void surf (Input IN, inout SurfaceOutput o) {
    			half4 c = tex2D (_MainTex, IN.uv_MainTex);
    			o.Albedo = c.rgb;
    			o.Alpha = c.a;
    		}
    		
    		//nafio phong光照公式 
    		//入射光反射向量的单位向量 r = 2*(n点乘l)*n
    		//反射光和视角方向的 差别用dot(reflectionVector,viewDir)表示
    		//镜面反射分量强度  pow(max(0,dot(reflectionVector,viewDir)),_SpecularPower);
    		//最后结果是漫反射结果+镜面反射结果
    		//总结phong实际计算公式就是  pow(r点乘v,材质高光系数)
    		//而bilnnPhong实际就是这个公式的简化 pow(n点乘h,材质高光系数),所以bilnnPhong更快
    		inline half4 LightingPhong(SurfaceOutput s,half3 lightDir,half3 viewDir,fixed atten){
    		 float diff = max(0,dot(s.Normal,lightDir));
    		 float3 reflectionVector = normalize(2.0 * s.Normal * diff - lightDir);
    		 float spec = pow(max(0,dot(reflectionVector,viewDir)),_SpecularPower);
    		 float3 finalSpec = _SpecularColor.rgb * spec;
    		 half4 c;
    		 c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten) + (_LightColor0.rgb * finalSpec);
    		 c.a = 1;
    		 return c;
    		}
    		
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    




    自定义BlinnPhong

    blinnPhong公式  I = _LightColor0.rgb  *  pow(N.H,SpecularPower)      H是L(顶点到光源)和V(顶点到视点)的半向量  H = (L+V)/|L+V|
    Shader "Nafio/NBlinn" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_MainTint("Diffuse Tint",Color) = (1,1,1,1)
    		_SpecularPower("Specular Power",Range(0.1,200)) = 1
    		_SpecularColor("Specular Color",Color) = (1,1,1,1)
    	}
    	
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf CustomBlinnPhong
    
    		sampler2D _MainTex;
    		float4 _MainTint;
    		float4 _SpecularColor;
    		float _SpecularPower;
    
    
    		struct Input {
    			float2 uv_MainTex;
    		};
    
    		void surf (Input IN, inout SurfaceOutput o) {
    			half4 c = tex2D (_MainTex, IN.uv_MainTex);
    			o.Albedo = c.rgb;
    			o.Alpha = c.a;
    		}
    		
    		inline half4 LightingCustomBlinnPhong(SurfaceOutput s,half3 lightDir,half3 viewDir,fixed atten){
    			fixed3 halfVector = normalize(lightDir + viewDir);
    			fixed diff = max(0,dot(s.Normal,lightDir));
    			fixed nh = max(0,dot(s.Normal,halfVector));
    			half spec = pow(nh,_SpecularPower);
    			half4 c;
    			c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten) +(_LightColor0.rgb * _SpecularColor.rgb * spec);
    			c.a = 1;
    			return c;
    		}
    		
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    



    Lit Sphere

    这个例子用了LitSphere(从光照图中读取光照)配合法线贴图
    Shader "Custom/LitSphere" {
    	Properties {
    		_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_NormalMap ("Normal Map", 2D) = "bump" {}
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf Unlit vertex:vert
    
    		float4 _MainTint;
    		sampler2D _MainTex;
    		sampler2D _NormalMap;
    		
    		inline half4 LightingUnlit (SurfaceOutput s, fixed3 lightDir, fixed atten)
    		{
    			half4 c = half4(1,1,1,1); 
    			c.rgb = s.Albedo;//直接使用了surf中的颜色结果(从贴图里得到的假光照),没计算真正的光照
    			c.a = s.Alpha;
    			return c;
    		}
    
    		struct Input {
    			float2 uv_MainTex;
    			float2 uv_NormalMap;
    			float3 tan1;
    			float3 tan2;
    		};
    		
    		void vert (inout appdata_full v, out Input o) 
    		{
    			UNITY_INITIALIZE_OUTPUT(Input,o);//Input类型变量o 置0,如果不是HSHL这句就是空的宏什么都不做
    			TANGENT_SPACE_ROTATION; //产生rotation这个矩阵并赋值的一段宏
    			o.tan1 = mul(rotation, UNITY_MATRIX_IT_MV[0].xyz);   //把观察坐标下的x轴转换到切向坐标系
    			o.tan2 = mul(rotation, UNITY_MATRIX_IT_MV[1].xyz);  //把观察坐标下的y轴转换到切向坐标系
    		}
    
    		void surf (Input IN, inout SurfaceOutput o)
    		{
    			float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
    			o.Normal = normals;
    			float2 litSphereUV;
    			litSphereUV.x = dot(IN.tan1, o.Normal);//tan1是从观察系被转换到切向坐标的x轴,Normal是法线贴图中法向量,法向量向x轴投,得到u
    			litSphereUV.y = dot(IN.tan2, o.Normal); //tan2是从观察系被转换到切向坐标的y轴,Normal是法线贴图中法向量,法向量向y轴投,得到v
    
    			//这里注意一点,上面dot的计算是因为用的是一张做好光照的贴图,简单说就是在这个<贴图中光方向是固定的,
    			//如果法线朝向接近光源的位置,就取图上更亮的一点,
    			//不好理解的是法线向观察系x,y坐标投影来决定使用图(含有光照方向)上哪一点
    					
    			half4 c = tex2D (_MainTex, litSphereUV*0.5+0.5);//*0.5+0.5是因为,dot的值是-1~1,而uv是0-1做了一下转换
    			o.Albedo = c.rgb * _MainTint;
    			o.Alpha = c.a;
    		}
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }

    重点解释如下两句代码
    TANGENT_SPACE_ROTATION; 
    o.tan1 = mul(rotation, UNITY_MATRIX_IT_MV[0].xyz);


    TANGENT_SPACE_ROTATION
    // Declares 3x3 matrix 'rotation', filled with tangent space basis
    #define TANGENT_SPACE_ROTATION 
    	float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; 
    	float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal )
    是宏换行连接符,
    v.normal就是切向坐标的z轴,
    v.tangent.xyz是切向坐标的x轴,
    v.tangent.w齐次坐标相关,值为1或-1,代表uv正向或是反向
    第一句实际是计算切向坐标的y轴
    第二句使用切向坐标x,y,z轴向量构建了从模型坐标转到切向坐标系的矩阵


    o.tan1 = mul(rotation, UNITY_MATRIX_IT_MV[0].xyz);

    这句作用是把观察坐标中的x轴转换到切向坐标,为了了解这句,需要趟几个坑


    1 mul函数
    (unity shader中的mul,gl和dx没确认过)
    第一种写法,参数1为矩阵,参数2为向量:  mul把向量当列向量
    第二种写法,参数1为向量,参数2为矩阵:  mul向量当为行向量
    比如最常见的写法,把点从模型空间转到世界空间,这里_Object2World就是针对列向量的矩阵,而v.vertex就是列向量
    mul(_Object2World,v.vertex)
    如果换成行向量就是
    mul(v.vertex,transpose(_Object2World))
    注意Unity默认一般用上面的写法,所以unity内部用的应该是列向量,_Object2World,_World2Object这类矩阵也都是跟列向量乘的

    2 从观察坐标转换到模型坐标

    unity没提供从观察系直接转换到切向坐标的矩阵,这里需要先从观察系转换到模型坐标
    具体方法就是
    mul(UNITY_MATRIX_VM, float3(1, 0, 0)) 等同于
    mul(float3(1,0,0),transpose(UNITY_MATRIX_VM))   
    而unity中并没有UNITY_MATRIX_VM(观察到模型的变换矩阵),但是有UNITY_MATRIX_IT_MV(模型到观察的逆转置),进一步变化
    mul(float3(1,0,0), (float3x3)UNITY_MATRIX_IT_MV)) 等同于
    UNITY_MATRIX_IT_MV[0].xyz
    其中1,0,0是观察系中的x轴,UNITY_MATRIX_IT_MV[0].xyz代表观察系中x轴在模型坐标系中的向量表示


    3 从模型坐标转换到切向坐标
    rotation * UNITY_MATRIX_IT_MV[0].xyz 就是 把模型坐标系中的一个向量(观察系的x轴)转换到切向坐标,结果就是切向坐标中的一个向量(观察系中的x轴)


    4 说下比较容易跟这个混淆的法线空间转换
    上面的3部是没有用到法线空间转换的
    法线的空间转换比较特殊,正常向量转换直接乘相应矩阵,但法向量无论向哪个空间转换
    除非这个转换矩阵不包含缩放,只要包含缩放的转换矩阵,直接乘法线会使法线不再垂直于原来的向量或平面
    解决方法就是法线向量要乘转换矩阵的逆转置矩阵,具体原因http://blog.csdn.net/onafioo/article/details/51889541
    乘逆转置矩阵是在保证法线垂直性不变的情况下推倒出来的,记住结论就可以。

    举例
    把普通向量从模型空间转换到世界空间
    float4 worldV = mul(_Object2World,v.vertex);
    把法向量从模型空间转换到世界空间,可以用如下两种方法,效果相同
    float4 worldNorm = mul(v.normal, _World2Object); 
    float4 worldNorm = mul(transpose(_World2Object), v.normal); 
    这里注意_World2Object 就是_Object2World的逆矩阵



    surf中
    litSphereUV.x = dot(IN.tan1, o.Normal);  这个计算方式 跟贴图相关
    使用的贴图是已经烘培好灯光效果的2d贴图(缺点是光照方向固定了不能再动态更改)

    这里对应关系可以这么理解,随便假定下观察系坐标,比如自己右是观察系x正向,前是z正向,上是y正向,那么右就是IN.tan1,随便找个物体放在面前,物体的法线方向就是
    o.Normal,当观察者不发生变化,而物体某个点转动,法线变化时,像素位置就会在贴图上变化


  • 相关阅读:
    NOIP2009-2018简要题解
    luogu P5023 填数游戏
    Java桌面精灵基础——swing类的使用与关键代码
    c信号处理程序以及setjmp函数longjmp函数的简单应用
    mmapcopy函数的编写
    nm命令的学习以及可执行文件中的段
    关于C中数组和指针的一点理解
    写一个简单的lisp解释器(1)
    House Robber
    SICP_3.31
  • 原文地址:https://www.cnblogs.com/nafio/p/9137475.html
Copyright © 2020-2023  润新知