shader学习笔记

Shader的简单结构

//声明shader所在的层级目录
Shader "CookbookShaders/BasicDiffuse"
{
    //输入参数
    Properties
    {
        //变量名(面板显示名,变量类型)=初始化值
        _EmissiveColor("Emissive Color",Color)=(1,1,1,1)
        _AmbientColor("Ambient Color",Color) = (1,1,1,1)
        _MySliderValue("This is a slider",Range(0,10)) = 2.5
    }
    //实际语义模块,可以有多个来对应不同的显卡配置
    SubShader
    {
        //标签栏目,这里表明渲染的类型是不透明的物体
        Tags { "RenderType"="Opaque" }
        //层次细节 200
        LOD 200

        //CG代码的开始声明
        CGPROGRAM
        //预处理指令,表明这是surface类型的shader,其函数名伪surf。此外指定了其光照模型为Lambert模型
        #pragma surface surf Lambert
        //声明输入变量
        float4 _EmissiveColor;
        float4 _AmbientColor;
        float _MySliderValue;
        //输入结构体
        struct Input
        {
            //主纹理的uv
            float2 uv_MainTex;
        };
        //着色函数
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c;
            //将两个颜色进行混合,并进行幕运算
            c = pow((_EmissiveColor+_AmbientColor),_MySliderValue);
            //将对应的颜色数据进行输出
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    //将阴影渲染相关的代码插入当前位置
    FallBack "Diffuse"
}

上面是一个简单的混色shader,简单的将两个颜色进行输入,最后将混合的颜色根据MySliderValue的调节来输出,具体效果如下:

cg中的数据类型

  1. float,32位浮点数据,一个符号位。浮点数据类型被所有的profile支持(但是directX8 pixel profiles 在一些操作种降低了浮点数的精度和范围);
  2. half,16位浮点数据;
  3. int,32位整形数据,有些profile会将int类型作为float类型使用;
  4. fixed,12位定点数,被所有的fragment profiles所支持;
  5. bool,被所有profiles支持。
  6. sampler*,纹理对象的句柄(the handle to a texture object),分为6类:sampler,sampler1d,sampler2d,sampler3d,samplerCUBE,和samplerRECT。

其中如float2、float3之类的则是二维和三维的数组

自定义漫反射光照模型

在subshader中我们曾经执行了一句预处理命令,声明我们要使用Lambert来处理光照效果,而现在我们要使用自己的光照方案来处理光照问题,对代码中的subshader部分进行修改,修改后应该是这样:

Shader "CookbookShaders/BasicDiffuse"
{
    Properties
    {
        _EmissiveColor("Emissive Color",Color)=(1,1,1,1)
        _AmbientColor("Ambient Color",Color) = (1,1,1,1)
        _MySliderValue("This is a slider",Range(0,10)) = 2.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        LOD 200

        CGPROGRAM
        //使用BasicDiffuse的光照系统来进行光照计算
        #pragma surface surf BasicDiffuse

        float4 _EmissiveColor;
        float4 _AmbientColor;
        float _MySliderValue;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c;
            c = pow((_EmissiveColor+_AmbientColor),_MySliderValue);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        //以Lignting+Name开头的就是所指定的光照计算方法
        //s:在surf中计算出的颜色
        //lightDir:顶点到光源的方向
        //atten:光源到顶点的衰变值
        inline float4 LightingBasicDiffuse(SurfaceOutput s,fixed3 lightDir,fixed atten)
        {
            //点积函数(dot)会将两个方向之间的夹角计算出来,取值在-1到1之间,如果是-1是背离,1是同向,0是垂直
            //因为如果是负数会导致出现严重的黑块出现,所以这里必须取值限制在0到1之间
            float difLight = max(0,dot(s.Normal,lightDir));

            float4 col;
            //将surf算出的颜色乘以_LightColor0中所获得的光源颜色,最后乘以倾斜角度和衰败对颜色的影响得出最后的颜色
            col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
            col.a = s.Alpha;
            return col;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

效果如下:

可以看出来相对于之前的模型,对于高光的反馈更明显一些。

Half Lambert光照模型

由Valve提出的改进方法,避免了低光区缺乏层次感,其实原理就是在计算明亮度时候,将0.5作为基准值,而变动则被限制在了-0.5到0.5之间,这也就意味着光线夹角的作用将会被削弱一半,但是会带来更好的视觉增强。具体修改如下:

inline float4 LightingBasicDiffuse(SurfaceOutput s,fixed3 lightDir,fixed atten)
        {
            //计算出夹角度,注意这里没有max,因为现在负值并不会导致负数,可以拿来使用
            float difLight = dot(s.Normal,lightDir);
            //将被缩小一半的夹角变量和0.5相加,得出优化后的夹角权重
            float hLambert = difLight * 0.5 + 0.5;

            float4 col;
            col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
            col.a = s.Alpha;
            return col;
        }

效果如下:

加了max的结果:(个人感觉max没必要加,不如都0.5起步拟真性不高,反而反向缺乏层次感了)

渐变纹理控制的漫反射

准备一张如下所示的色图:

然后修改代码成为如下样式,将色图应用到夹角计算中:

Shader "CookbookShaders/BasicDiffuse"
{
    Properties
    {
        _EmissiveColor("Emissive Color",Color)=(1,1,1,1)
        _AmbientColor("Ambient Color",Color) = (1,1,1,1)
        _MySliderValue("This is a slider",Range(0,10)) = 2.5
        //声明一个2d图像输入量
        _RampTex("Ramp Texture",2D) = ""
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        LOD 200

        CGPROGRAM
        #pragma surface surf BasicDiffuse

        float4 _EmissiveColor;
        float4 _AmbientColor;
        float _MySliderValue;
        //声明数据类型
        sampler2D _RampTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c;
            c = pow((_EmissiveColor+_AmbientColor),_MySliderValue);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        inline float4 LightingBasicDiffuse(SurfaceOutput s,fixed3 lightDir,fixed atten)
        {
            float difLight = dot(s.Normal, lightDir);
            float hLambert = difLight * 0.5 + 0.5;
            //使用tex2D读取在色图中指定2d坐标的颜色,第二个参数就是要读取的颜色
            float3 ramp = tex2D(_RampTex, float2(hLambert, hLambert)).rgb;

            float4 col;
            //将原来的物理因素全部排除,由色图来决定光照效果
            col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
            col.a = s.Alpha;
            return col;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

最后实现的效果:

有趣的一点是,要注意色图的分辨率问题,tex2D是依据像素点来读取rgb的,假如你像素过高,则都会显示为一个色域,看起来就跟实现失败一样。此外我看的参考消息中float只有一个参数,而实际上unity中的语法里,float必须两个参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值