2018-08-04 【shader】学习

【学习链接】

冯乐乐shader https://blog.csdn.net/candycat1992/article/details/44039077
OpenGLShader技巧集合:https://blog.csdn.net/stalendp/article/details/18745663
shader集锦:https://blog.csdn.net/tjw02241035621611/article/details/80038608
超超超牛逼大神主页 http://www.iquilezles.org/www/index.htm

抠脚大佬https://blog.csdn.net/qq_16756235

基础理论:https://www.cnblogs.com/lixiang-share/p/5025662.html
一堆好看的场景浅墨:https://blog.csdn.net/poem_qianmo/article/details/40955607?utm_source=blogxgwz1
unityshader内置函数:https://blog.csdn.net/a6627651/article/details/50680360

shader例子:
为队伍设置不同颜色shader https://www.cnblogs.com/jackmaxwell/p/6220511.html

rayMathing算法:http://www.taikr.com/app.php/article/3880
https://blog.csdn.net/w450468524/article/details/51699086
rayMatching框架:https://blog.csdn.net/tjw02241035621611/article/details/80057928


【Unity Shader的分类】

使用的是ShaderLab编写Unity中的Shader

1.表面着色器 Surface Shader
(Unity独有,对开发人员很方便,对顶点片元做了一层封装 光源较多时,适合使用这个)
2.顶点/片元着色器 Vertex/Fragment Shader
3.固定函数着色器 Fixed Function Shader(很老了)

image.png

【基本语法】

    Shader "Wen/01 myshader"{       //这里指定shader的名字,不要求跟文件名保持一致
        Properties{
            //属性
            _Color("Wen_Color",Color)=(1,1,1,1)     //float4
            _Vector("Vector",Vector)=(1,2,3,4)      //float4
            _Int("Int",Int)=123456  //float
            _Float("Float",Float)=3.14159   //float
            _Range("Range",Range(-2,2))=0.2     //float
            _2D("Texture",2D)="red"{}       //若未指定图片,使用该默认的纯色   sampler2D
            _Cube("Cube",Cube)="white"{}        //立方体贴图(例天空盒)samplerCube
            _3D("Texture",3D)="black"{} //3D纹理  samper3D
        }

        //子Shader模块,可以有很多个 显卡运行效果的时候,从第一个SubShader开始,
        //如果第一个SubShader里面的效果都可以实现,那么就使用第一个,否则自动尝试下一个
        SubShader{
            Pass{
                //可以有很多Pass块,每一个都代表一个方法 至少有一个Pass
                CGPROGRAM
                //使用CG语言编写Shader代码 (HLSLPROGRAM)
                //上面声明的变量,需要到这里面重定义一下。

                float4 _Color;  //可以使用 half fixed代替 
                                        //float  32位存储 
                                        //half 16位存储 -6万-+6万
                                        //fixed 11位存储 -2-+2 一般颜色都用这个值来存储,节约空间
                float4 _Vector;
                float _Int;
                float _Float;
                float _Range;
                sampler2D _2D;
                samplerCube _Cube;
                sampler3D _3D;

                ENDCG
            }

        }

        //若全部失败,默认效果
        Fallback "Legacy Shaders/VertexLit"
    }

【第一个函数 绘制纯色】

    // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

    Shader "Wen/02 secondshader can run"{
        Subshader{
            Pass{
                CGPROGRAM
                //顶点函数 这里只是声明了顶点函数的函数名
                //基本作用:完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
                #pragma vertex vert

                //片元函数 这里只是声明了片元函数的函数名
                //基本作用 返回模型对应的屏幕上的每一个像素的颜色值
                #pragma fragment frag


                        float4 vert(float4 v : POSITION) :SV_POSITION {//通过语义告诉系统,这个参数是干嘛的
                            //比如POSITION是告诉系统我需要顶点坐标
                            //SV_POSITION是解释说明返回值,意思是返回值是裁剪空间下的顶点坐标
                            float4 pos = UnityObjectToClipPos(v );
                            return pos;
                        }

                        fixed4 frag() :SV_Target{
                            return fixed4(1,1,1,1);
                        }

                ENDCG
            }
        }
        Fallback "Legacy Shaders/VertexLit"
    }

【语义】

(类似C#中的attribute,给变量作说明用的。一般这玩意都写在结构体中,使得结构更清晰)

语义的分类

从应用程序传递到顶点函数的语义:
POSITION 传递顶点坐标(模型空间)
NORMAL 法线(模型空间)
TANGENT 切线(模型空间)
TEXCOORD0~N 纹理坐标
COLOR 顶点颜色

从顶点函数传递到片元函数的语义:
SV_POSITION 裁剪空间中的顶点坐标(一般系统使用)
COLOR0 语义上是传递颜色,但实际上一般都是传递一组值4个
COLOR1 同COLOR0
TEXCOORD0~7 语义上是传递纹理坐标,但不是一定,可以传别的

从片元函数传递给系统:
SV_TARGET 颜色值,表示将要渲染的颜色值

上例通过语义格式修改:

    // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

    Shader "Wen/02 secondshader can run"{
        SubShader{
            Pass{
                CGPROGRAM
                //顶点函数 这里只是声明了顶点函数的函数名
                //基本作用:完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
                #pragma vertex vert

                //片元函数 这里只是声明了片元函数的函数名
                //基本作用 返回模型对应的屏幕上的每一个像素的颜色值
                #pragma fragment frag

                //application to vertex
                struct a2v{
                    float4 vertex : POSITION;   //该语义会让系统把模型空间的顶点的值,自动传给vertex
                    float3 nomal : NORMAL;  //该语义会让系统把模型空间的法线方向的值,自动传给nomal
                    float4 texcoord : TEXCOORD0;    //该语义会让系统把模型的第一套纹理坐标的值传给texcoord
                };

                //vertex to fragment
                struct v2f{
                    float4 position : SV_POSITION;  //裁剪空间中的顶点坐标(一般系统使用)
                    float3 temp : COLOR0;     //返回值,必须有语义 不然程序会报错 这里的语义可以自己定
                };

                //fragment to application
                struct f2a{
                    fixed4 target : SV_TARGET;
                };


                //顶点坐标=====>>>> 从程序获取,从顶点传递给片元,片元返回给程序

                v2f vert(a2v _v)
                {
                    v2f _f;
                    _f.position = UnityObjectToClipPos(_v.vertex);  //获得模型顶点坐标,转化为裁剪坐标,并将该裁剪坐标赋值给 _f结构中得顶点
                    _f.temp = _v.nomal; //法线坐标传过来,赋值
                    return _f;
                }

                //上一步执行得返回值作为参数传递进来
                f2a frag(v2f _f)
                {
                    f2a _a;
                    _a.target = fixed4(f.temp,1);   //顶点传过来的法线坐标,根据此坐标为每个像素的颜色赋值;
                                                                //法线坐标中间的像素颜色,会使用插值自动赋值;
                                                                //并没什么意义,可以通过颜色判断该点的法线坐标值
                    return a;
                }

                ENDCG
            }
        }
        Fallback "Legacy Shaders/VertexLit"
    }

【内置函数及导入头文件函数】

image.png

image.png

image.png

pow max dot reflect normalize


image.png

【光照模型】

光照模型就是一个公式,使用这个公式来计算某个点的光照效果

标准光照模型

在标准光照模型里面,我们把进入摄像机的光分为下面四个部分:

自发光
高光反射 Specular(镜面反射)

Specular = 直射光 * pow(max(0,cos夹角(反射光和视野方向夹角)),10) cos夹角 = normalize(反射光方向)·normalize(视野方向)

漫反射

兰伯特:Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角)) cos夹角 = normalize(光方向)·normalize(法线方向)
半兰伯特:Diffuse = 直射光颜色 * (cos夹角*0.5+0.5) -0.5~0.5 + 0.5,避免了夹角过大时候全部算0

环境光 Ambient

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; 相加就得到叠加的颜色,相乘是混合的颜色

Blinn-Phong光照模型(上面那个高光Blinn的改进)

反射光和视野方向夹角====》改为法线和x的夹角;x是平行光和视野方向的平分线。


image.png
  • Tips:
    Tags{ "LightMode"="ForwardBase" }
    只有定义了正确的LightMode才能得到一些Unity的内置光照变量。
    #include "Lighting.cginc"
    包含unity的内置文件,才可以使用unity内置的一些变量

漫反射示例

物体本身颜色叠加漫反射颜色,用乘法;添加环境光照,亮度增强,用加法。


后面逐顶点,前面逐片元漫反射

最右侧HalfLambert

高光反射示例

fixed3 specular = _LightColor0.rgb * pow(max(0,dot(specularDir(反射角),camDir)),10);
//添加环境光 (亮度增强,用加)
diffuse = diffuse + ambient + specular;
fixed3 specularDir = 2 * dot(lightDir, normalDir)*normalDir - lightDir;
【镜面反射与phong模型】https://blog.csdn.net/xuexiaokkk/article/details/49999041

其实。只能感受到顶点片元的明显区别

Fresnel,反射,折射

基本原理:用一个相机在物体位置拍周围的环境,保存为cubemap;通过视角和法线,获得从视角位置的反射/折射光方向,
菲涅尔和反射颜色处理方式相同:fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;

  • 反射是直接把菲涅尔强度的计算写成一个固定的数值
  • 菲涅尔是根据视角和法线方向进行计算这个数值:fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);


    image.png

Grabpass抓取屏幕纹理

如果想要获得透明效果后面的屏幕显示图片,并对其进行操作,可以通过Grabpass方式(也可以通过Rendertexture,以及命令缓冲(Command Buffers))。


image.png
Shader "Wen/17-GlassRfraction" {

Properties{
    _MainTex("Main Tex",2D) = "white"{}
    _BumpMap("Normal Map",2D) = "bump"{}
    _Cubemap("Environment Cube Map",cube) = "_Skybox"{}
    _Distortion("Distortion", Range(0, 100)) = 10
    _RefractAmount("Refract Amount", Range(0.0, 1.0)) = 1.0
}
SubShader{
    // We must use Transparent, so other objects are drawn before this one;
    Tags { "RenderType" = "Opaque" "Queue" = "Transparent"}

    // This pass grabs the screen behind the object into a texture.
    // We can access the result in the next pass as _RefractionTex
    GrabPass{"_RefractionTex"}

    Pass {
        Tags { "LightMode" = "ForwardBase" }

        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"

        sampler2D _MainTex;
        float4 _MainTex_ST;
        sampler2D _BumpMap;
        float4 _BumpMap_ST;
        samplerCUBE _Cubemap;
        float _Distortion;
        fixed _RefractAmount;
        sampler2D _RefractionTex;
        float4 _RefractionTex_TexelSize;

        struct a2v {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 tangent : TANGENT;
            float2 texcoord : TEXCOORD0;
        };

        struct v2f {
            float4 pos : SV_POSITION;
            float4 scrPos : TEXCOORD0;
            float4 uv : TEXCOORD1;
            float4 TtoW0 : TEXCOORD2;
            float4 TtoW1 : TEXCOORD3;
            float4 TtoW2 : TEXCOORD4;
        };

        v2f vert(a2v v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.scrPos = ComputeGrabScreenPos(o.pos);
            o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
            o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);

            float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
            fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
            fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;

            o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
            o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
            o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

            return o;
        }

        fixed4 frag(v2f i) : SV_Target {
            float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
            fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));

            // Get the normal in tangent space
            fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));

            // Compute the offset in tangent space
            float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
            i.scrPos.xy += offset * i.scrPos.z;
            fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;

            // Convert the normal to world space
            bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
            fixed3 reflDir = reflect(-worldViewDir, bump);
            fixed4 texColor = tex2D(_MainTex, i.uv.xy);
            fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;

            fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;

            return fixed4(finalColor, 1);
        }

        ENDCG
    }
}
FallBack "Reflective/VertexLit"

}

上面涉及到的知识点:从切线空间转换到世界空间。这个乘法很迷幻。以后找个人问问吧。

image.png

话接上文。从切线空间转世界空间一般都是为了计算法线光照的。存两篇文章。
http://www.cnblogs.com/lancidie/p/7827434.html
https://en.wikibooks.org/wiki/Cg_Programming/Vector_and_Matrix_Operations

你可能感兴趣的:(2018-08-04 【shader】学习)