• Unity Shaders and Effects Cookbook (3-4) 使用高光贴图


    在学习完上一节之后。已经了解了在Unity 中怎样实现一个高光 Shader ,可是会有一个问题。就是效果看起来不切实际,如以下的问题


    我用一张图片贴到了Cube上面。然后用了一个高光材质,得到了下图的效果。



    事实上这个效果还算能够,可是认真看就会发现。这个结果是不符合自然现象的。

    这个箱子是木头的,然后有铁皮 作为封条。

    首先不符合常理的是为什么这个木头箱子会反光!

    可能木头箱子打蜡了。然后就反光。可是为什么打蜡的木头 和 铁皮 看起来是一样的。光滑度是一样的吗?

    转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

    这样一种效果是非常难解释的通的。

    那么怎样模拟真实的情况。也就是该反光的地方才反光。不该反光的地方不反光?


    回忆一下高光的原理。高光是依据反射光与 视线的角度来求出高光的强度值的。对于上面的箱子,木材 和 铁片是在同一个平面上的,所以求出的高光强度值是同样的。

    也就是说。依照上一节的做法是不能将 铁片 和 木材的高光强度值区分开来的。

    那么我们要想一个办法。

    首先想到的是,把铁片 和 木材分开来。木材作为单独的一张贴图,铁片作为另外一张贴图,里面是空白的。

    灯光仅仅作用于铁片这一张贴图。我们计算出来的 高光强度 Specular * 铁片贴图的RGB。由于铁片中间是黑色的,所以铁片的中间这一块的 RGB 都是 0 。所以实际上仅仅有外側铁片的地方,才真正受到了光照的影响!然后再和 木材的贴图的颜色相加。

    转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

    由此引入这一节的知识 -- 高光贴图。

    如上面所说,须要两个贴图,木材这一个贴图仅仅接受漫反射光照。而铁片这一个高光贴图 接收高光。

    在 Shader 中定义相应的变量

    Properties 
    {
    	_MainTex ("Base (RGB)", 2D) = "white" {}
    
    	_SpecularColor("Specular Color",Color)=(1,1,1,1)
    
    	_SpecularTexture("Specular Texture",2D)="white" {}
    
    	_SpecularPower("Specular Power",Range(0.1,100))=1
    }


    我们在 Suf 函数中,对两个纹理取样。然后存储到 SurfaceOutput 结构体中传入到 光照模型函数。


    然后会遇到一个问题,SurfaceOutput 结构体,是Unity 定义的一个结构体,其定义存在与 Lighting.cginc 文件里

    struct SurfaceOutput {
    	fixed3 Albedo;
    	fixed3 Normal;
    	fixed3 Emission;
    	half Specular;
    	fixed Gloss;
    	fixed Alpha;
    };

    查看法线。里面并没实用于存储高光贴图颜色信息的变量!

    所以这次我们要自己定义一个 SurfaceOutput 结构体,加入一个 SpecularColor 变量。

    struct CustomSurfaceOutput 
    {
    	fixed3 Albedo;
    	fixed3 Normal;
    	fixed3 Emission;
    	half Specular;
    	fixed3 SpecularColor;
    	fixed Gloss;
    	fixed Alpha;
    };

    然后把 surf 和 光照模型函数中的 SurfaceOutput 都改动为自己定义的 CustomSurfaceOutput

    void surf (Input IN, inout CustomSurfaceOutput o) 
    {
    	half4 c = tex2D (_MainTex, IN.uv_MainTex);
    	o.Albedo = c.rgb;
    	o.Alpha = c.a;
    }
    
    
    inline fixed4 LightingCustomPhong(CustomSurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten)
    {
    	fixed4 c;
    	c.rgb=s.Albedo;
    	c.a=s.Alpha;
    	return c;
    }

    注意。这个时候我们还没有指定光照模型函数为 CustomPhong,所以Unity 会抛出一堆莫名其妙的错,这是由于 surf 中传给 Lambert 光照模型的是 CustomSurfaceOutput,而不是默认的 SurfaceOutput 了。

    转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

    指定光照模型为 CustomPhong

    CGPROGRAM
    #pragma surface surf CustomPhong

    由于要在 Surf 函数中处理 木头 这个 漫反射贴图 和  铁皮 这个高光贴图,原来的 Input 结构体中是仅仅有 漫反射贴图的 UV信息的,所以改动 Input结构体加入高光贴图的 UV信息

    struct Input 
    {
    	float2 uv_MainTex;
    	float2 uv_SpecularTexture;
    };

    改动 surf 函数,依据 Input 中的UV信息,提取当前 UV坐标的颜色信息(纹素)

    void surf (Input IN, inout CustomSurfaceOutput o) 
    {
    	//不接受高光的,漫反射贴图,比如木头
    	half4 c = tex2D (_MainTex, IN.uv_MainTex);
    	o.Albedo = c.rgb;
    	o.Alpha = c.a;
    
    	//接收高光的,高光贴图,比如铁皮
    	half4 specularC=tex2D(_SpecularTexture,IN.uv_SpecularTexture);
    	o.SpecularColor=specularC.rgb;
    
    	//用r值作为系数,假设当前UV坐标是位于铁片里面黑色的那一块。那么rgb都是0。这样里面黑色的那一块事实上是无效的。
    	o.Specular = specularC.r;     
    }


    改动光照函数

    inline fixed4 LightingCustomPhong(CustomSurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten)
    {
    
    	//首先计算漫反射;
    	float diffuse=max(0,dot(s.Normal,lightDir));
    
    	//计算漫反射颜色;
    	float3 diffuseColor=_LightColor0*s.Albedo * diffuse;
    
    	//计算反射光方向向量
    	float3 halfReflectVector=normalize(lightDir + viewDir);
    
    	//计算反射光强度;假设当前位置是铁片黑色的那一块,那么Specular是0,这里就没有高光了。
    	float specular = pow( max(0,dot(s.Normal,halfReflectVector)) , _SpecularPower) * s.Specular;
    
    	//计算高光颜色  高光贴图採样颜色 * 反射光强度 * 编辑器中指定的高光颜色 * 光照颜色;
    	float3 specularColor =_LightColor0.rgb* s.SpecularColor * specular * _SpecularColor.rgb *(atten*5);
    
    
    	fixed4 c;
    	c.rgb=diffuseColor + specularColor;
    	c.a=s.Alpha;
    	return c;
    }

    终于完毕得到结果



    演示样例project下载:

    http://pan.baidu.com/s/1dFyiyDb





  • 相关阅读:
    原创:Qt自定义拖放
    看下最近公司的招聘需求
    leveldb阅读心得
    Relationship between the FIX Protocol's OrdID, ClOrdID, OrigClOrdID?
    Wait Functions
    全局变量与单例模式
    Asynchronous I/O
    QuickFix MsgHandler
    第一个Java程序
    (原創) Function Pointer、Delegate和Function Object (C/C++) (template) (.NET) (C#)
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7228148.html
Copyright © 2020-2023  润新知