IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Unity3D游戏开发从零单排(十) - 进击的Shader续

    qp120291570发表于 2015-04-11 21:58:26
    love 0

    自定义光照模型

    在这之前首先来了解一下SurfaceOutput这个结构体,它是一个包含大多数描述一个物体表面渲染特征的结构,具体结构如下:

    struct SurfaceOutput {
    	half3 Albedo;//纹理颜色
    	half3 Normal;//法线
    	half3 Emission;//自发光,不受照明的影响
    	half Specular;//高光指数
    	half Gloss;//光泽度
    	half Alpha;//Alpha通道
    };

    基本上所有的Shader函数要处理的就是这个结构体。


    Unity自带的光照实现都定义在一些*.cginc文件中,要自定义光照模型,只要不用Unity自带的光照模型就可以了。

    将下面这一行的语句的最后替换成对应的光照计算函数。

    #pragma surface surf NoLight


    无光照的材质Shader

    Shader "Custom/NoLight" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf NoLight
    
    		sampler2D _MainTex;
    		inline float4 LightingNoLight(SurfaceOutput s, fixed3 lightDir, fixed3 atten)
    		{
    			float4 col;
    			col.rgb = s.Albedo;
    			col.a = s.Alpha;
    			return col;
    		}
    
    
    		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;
    		}
    
    
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    


    自己实现一个diffuse


    inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed 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;
    }





    上图中左边是NoLight,右边是SimpleDiffuse。


    同理可以自定义实现各种光照模型了,Lambert,Blinning,,,,,


    补充一下光照函数的几种写法

    half4 LightingName (SurfaceOutput s, half3 lightDir,half atten){}

    这个函数用于不需要视角方向的情况下的Forward rendering。

    half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten){}

    这个函数用于需要视角方向的情况下的Forward rendering。

    half4 LightingName_PrePass (SurfaceOutput s, half4 light){}

    这个函数用于Deferred rendering。


    用代码控制shader动态修改材质

    对SimpleDiffuse稍微做一下修改,添加一个叠加的颜色。

    Shader "Custom/SimpleDiffuse" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_Color ("Main Color", Color) = (0,1,0,1)
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf Lambert
    
    		sampler2D _MainTex;
    		float4 _Color;
    
    		struct Input {
    			float2 uv_MainTex;
    		};
    
    		void surf (Input IN, inout SurfaceOutput o) {
    			half4 c = tex2D (_MainTex, IN.uv_MainTex);
    			o.Albedo = c.rgb * _Color.rgb;
    			o.Alpha = c.a;
    		}
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    

    在模型(有MeshRenderer的)上面挂一个脚本,实现如下

    MeshChanger

    using UnityEngine;
    using System.Collections;
    
    public class MaterialChanger : MonoBehaviour {
    	float time = 0.0f;
    	float changeSpeed1 = 2.0f;
    	float changeSpeed2 = 5.0f;
    	Renderer render;
    
    	// Use this for initialization
    	void Start () {
    		render = transform.GetComponent();
    	}
    	
    	// Update is called once per frame
    	void Update () {
    		float v1 = (Mathf.Cos(time * changeSpeed1) + 1.0f) * 0.5f;
    		float v2 = (Mathf.Sin(time * changeSpeed2)+ 1.0f) * 0.5f;
    
    		render.materials[0].SetColor("_Color", new Color(v1, v2, v2));
    		time += Time.deltaTime;
    	}
    }
    

    根据时间动态修改0号材质的_Color选项,结果就像这样





    自行脑补中间过程。


    使用渐变纹理来处理光照

    首先要准备一张渐变纹理,原理就是通过计算当前位置的光照与法线的点积,索引到渐变图片上的像素值,最后将其和diffuse叠加。


    Shader "Custom/RampTexture" {
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_RampTex ("Ramp Tex (RGB)", 2D) = "white" {}
    
    	}
    	SubShader {
    		Tags { "RenderType"="Opaque" }
    		LOD 200
    		
    		CGPROGRAM
    		#pragma surface surf RampLight
    
    		sampler2D _MainTex;
    		sampler2D _RampTex;
    
    		half4 LightingRampLight (SurfaceOutput s, half3 lightDir, half atten) 
    		{
    			half NdotL = dot(s.Normal, lightDir);
    			float diff = NdotL * 0.5 + 0.5;
    			half3 ramp = tex2D(_RampTex, float2(diff, diff)).rgb;
    			half4 c;
    			c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
    			c.a = s.Alpha;
    			return c;
    		}
    
    		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;
    		}
    		ENDCG
    	} 
    	FallBack "Diffuse"
    }
    

    渲染结果



    没有纹理的情况(是大象不是小猪)




    换一下索引贴图

    得到结果如下(Toon Shading)



    参考

    Unity Shaders and Effects CookBook




沪ICP备19023445号-2号
友情链接