Unity笔记 Surface Shader

来源:互联网 发布:网络文明传播志愿活动 编辑:程序博客网 时间:2024/05/02 02:59

学习《unity shaders and effects book》的tips。

拿到代码后就可以开始了,看代码比看书快很多。
配套代码csdn下载

Nvidia cg tutorial

预备:
Unity 5 的stand shader
学习这个可以做为一种效果参照,并且熟悉相关概念。
Main Maps
Albedo,diffuse纹理,可以设置tintcolor和一张纹理。
Metallic,设置材质的金属感大小。贴图R通道可以指的metallic。A通道制定smoothness。G、B没有使用。
Specular,和metallic二选一。用于设置反射环境的程度。
对比metallic和specular
Normal Map,法线贴图,可以设置负数。
Height Map,高度贴图。
Occlusion,屏蔽反光区域。
Emission,发光贴图,可以是彩色的,用于模拟显示器。
Detail Mask,用于控制第二贴图。
Secondary Maps
细节题图。

简单例子注解

Shader "CookbookShaders/Chapter1/BasicDiffuse" // 在材质选shader时的路径{    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 // 颜色控制参数,这里用一个范围slider    }    SubShader // 一段备选shader代码    {        Tags { "RenderType"="Opaque" } // 设置为“不透明体”,这会影响渲染顺序        LOD 200 // 这个值越高对应效果越复杂的shader[手册](http://docs.unity3d.com/Manual/SL-ShaderLOD.html)        Cull Back // 这个位置可以设置标识[手册](http://docs.unity3d.com/Manual/SL-CullAndDepth.html)        CGPROGRAM // 开始CG代码        #pragma surface surf BasicDiffuse // surface函数是surf 使用自定义光照处理函数LightingBasicDiffuse         // 颜色控制参数,这里要定义一套一样的        float4 _EmissiveColor;        float4 _AmbientColor;        float _MySliderValue;        // 自定光照处理,这里注意这个函数在surf后执行,返回一个像素的颜色,s是surf的结果,lightDir是unity帮你算好这个点上的光方向,atten光衰减        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;        }        // 自定义surf需要的一个像素的信息,需要的如果unity有就加上        struct Input         {            // 主纹理的uv,其实也没有用到            float2 uv_MainTex;        };        // 表明函数,vs之后,ps阶段,LightingBasicDiffuse 之前,IN输入,o输出(成员固定,和IN不同)        void surf (Input IN, inout SurfaceOutput o)         {            // 根据材质里的设置生成一个颜色            float4 c;            c =  pow((_EmissiveColor + _AmbientColor), _MySliderValue);            // 赋值给输出的Albedo成员(diffuse color)            o.Albedo = c.rgb;            // 透明度给Alpha            o.Alpha = c.a;        }        ENDCG // 结束CG    }     FallBack "Diffuse" // 所有subshader都失败,执行这个shader}

用到的数据类型
float: high precision floating point. Generally 32 bits, just like float type in regular programming languages.
half: medium precision floating point. Generally 16 bits, with a range of –60000 to +60000 and 3.3 decimal digits of precision.
qualcomm的人建议使用,但是实际应用中不能保证兼容性。
fixed: low precision fixed point. Generally 11 bits, with a range of –2.0 to +2.0 and 1/256th precision.
fixed用于颜色,方向什么的足够了
unity3种shader编写方式:
固定管线(设置一些开关为主)
surface shader(本书主要介绍的)
shader(也不是原生的shader代码,最大灵活度)
unity3种rendering path:
forward(最普通,一个一个的渲染物体然后blend)
deferred(延迟渲染,需要多个rendertaget减少ps重复计算效率高,暂时移动平台支持的不好)
vertex lit(逐顶点光照这个快,相对于逐像素光照)
struct Input成员可制定的输入结构体:
用到再声明。
float3 viewDir - will contain view direction, for computing Parallax effects, rim lighting etc.
float4 with COLOR semantic - will contain interpolated per-vertex color.
float4 screenPos - will contain screen space position for reflection or screenspace effects.
float3 worldPos - will contain world space position.
float3 worldRefl - will contain world reflection vector if surface shader does not write to o.Normal. See Reflect-Diffuse shader for example.
float3 worldNormal - will contain world normal vector if surface shader does not write to o.Normal.
float3 worldRefl; INTERNAL_DATA - will contain world reflection vector if surface shader writes to o.Normal. To get the reflection vector based on per-pixel normal map, use WorldReflectionVector (IN, o.Normal). See Reflect-Bumped shader for example.
float3 worldNormal; INTERNAL_DATA - will contain world normal vector if surface shader writes to o.Normal. To get the normal vector based on per-pixel normal map, use WorldNormalVector (IN, o.Normal).
SurfaceOutput成员固定的输出结构体:
struct SurfaceOutput
{
fixed3 Albedo; // diffuse color
fixed3 Normal; // tangent space normal, if written
fixed3 Emission;
half Specular; // specular power in 0..1 range
fixed Gloss; // specular intensity
fixed Alpha; // alpha for transparencies
};
unity 5
struct SurfaceOutputStandard
{
fixed3 Albedo; // base (diffuse or specular) color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Metallic; // 0=non-metal, 1=metal
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};
struct SurfaceOutputStandardSpecular
{
fixed3 Albedo; // diffuse color
fixed3 Specular; // specular color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};

第一章
BasicDiffuse
定义了#pragma surface surf BasicDiffuse
LightingBasicDiffuse 代替了默认的光照。
自定义的三种光照模型方式,选自己需要的:
1)half4 Lighting (SurfaceOutput s, half3 lightDir, half atten);
This is used in forward rendering path for light models that are not view direction dependent (e.g. diffuse).
2)half4 Lighting (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);
This is used in forward rendering path for light models that are view direction dependent.
3)half4 Lighting_PrePass (SurfaceOutput s, half4 light);
This is used in deferred lighting path.

实验:加入多个灯光,设置Lighting中的Ambient Color(主环境色)都会对这个shader产生影响。
结论:inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)其中的 lightDir是Unity计算好多个光源后的结果(针对每个像素),这可以直接输出lightDir到最终颜色来验证。
col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
这里使用的s.Albedo是surf的结果。
类似lightDir,_LightColor0.rgb也是多个光源的结果值。加入点光后可验证forwordbase行为(注意调整 render mode)。unity shader中光线是一块要展开学习的内容手册。
这里forword来说2个pass种类:
1)base是1个pixel级别光+4个vertex lit+n个SH。
2)add如果还需要pixel级别的在这个pass处理,每加一个会多一个pass来处理。
atten 指的是衰减attenuation。

执行顺序是先surf 后LightingBasicDiffuse,这两个都是ps阶段(每个像素处理)。

HalfLambertDiffuse
float hLambert = difLight * 0.5 + 0.5; 而已。

BRDFDiffuse
这个shader相当巧妙,效果佳、效率高、好控制。
用一张贴图实现了2灯和3灯布光。
1维采样,2灯
float difLight = dot (s.Normal, lightDir);
float hLambert = difLight * 0.5 + 0.5;
float3 ramp = tex2D(_RampTex, float2(hLambert,hLambert)).rgb;
2维采样,3灯,viewDir的使用。
float difLight = dot (s.Normal, lightDir);
float rimLight = dot(s.Normal, viewDir);
float hLambert = difLight * 0.5 + 0.5;
float3 ramp = tex2D(_RampTex, float2(hLambert, rimLight)).rgb;
这里写图片描述

第二章
ScrollingUVs

// 先存原始UVfixed2 scrolledUV = IN.uv_MainTex;// _Time是unity built-in的值 http://docs.unity3d.com/Manual/SL-UnityShaderVariables.htmlfixed xScrollValue = _ScrollXSpeed * _Time;fixed yScrollValue = _ScrollYSpeed * _Time;// 做偏移scrolledUV += fixed2(xScrollValue, yScrollValue);// 这里用 _MainTint 来染色half4 c = tex2D (_MainTex, scrolledUV);o.Albedo = c.rgb * _MainTint;o.Alpha = c.a;

AnimatedSprite
利用cs脚本传入一个时间参数
timeValue = Mathf.Ceil(Time.time % 16);
transform.GetComponent().material.SetFloat(“_TimeValue”, timeValue);
需要在Properties里加_TimeValue (“Time Value”, float) = 0.0

TextureBlending
WOW就是这样笔刷地形贴图的方式(贴图本身的alpha也没浪费,用做反光),试想一个tile模型上多种地形交叉,并且还要铺条路。
把各层(草、石头、路面)纹理编辑结果(8bit是0~255级)放在一张贴图的各个Channel里。
但是5.0版本编译这个shader有个问题5张贴图呀,要加#pragma target 4.0才成。手册
此处如何拆分处理需要花时间研究,先mark一下吧。继续看……

NormalMapping
unpacknormal来计算正确的法线。

ProceduralTexture
纹理生成方式在cs脚本中。

PhotoshopLevels
本章花时间理解算法挺好,毕竟是个效果处理的基础。
shader中定义了一个函数来减少代码

float GetPixelLevel(float pixelColor){    float pixelResult;    pixelResult = (pixelColor * 255.0);    pixelResult = max(0, pixelResult - _inBlack);    pixelResult = saturate(pow(pixelResult / (_inWhite - _inBlack), _inGamma));    pixelResult = (pixelResult * (_outWhite - _outBlack) + _outBlack)/255.0;        return pixelResult;}

第三章
BlinnPhong
使用unity的光照模型:BlinnPhong或Lambert

#pragma surface surf BlinnPhong

Phong
使用需要视点方向的自定光照模型:
inline fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
计算出反光向量:
这里写图片描述

//Calculate diffuse and the reflection vectorfloat diff = dot(s.Normal, lightDir);float3 reflectionVector = normalize((2.0 * s.Normal * diff) - lightDir);//Calculate the Phong specularfloat spec = pow(max(0,dot(reflectionVector, viewDir)), _SpecPower);float3 finalSpec = _SpecularColor.rgb * spec;//Create final colorfixed4 c;c.rgb = (s.Albedo * _LightColor0.rgb * diff) + (_LightColor0.rgb * finalSpec);c.a = 1.0;return c;

CustomBlinnPhong
jim blinn带入cg世界的算法,高效且效果好。
半角矢量halfVector。

float3 halfVector = normalize (lightDir + viewDir);float diff = max (0, dot (s.Normal, lightDir));float NdotH = max (0, dot (s.Normal, halfVector));float spec = pow (NdotH, _SpecPower);float4 c;c.rgb = (s.Albedo * _LightColor0.rgb * diff) + (_LightColor0.rgb * _SpecularColor.rgb * spec) * (atten * 2);c.a = s.Alpha;return c;

SpecularMask
使用R通道当成反光强度,rgb做为颜色

//Set the parameters in the Output Structo.Albedo = c.rgb;o.Specular = specMask.r;o.SpecularColor = specMask.rgb;o.Alpha = c.a;

s.Specular参与运算

float spec = pow(max(0.0f,dot(reflectionVector, viewDir)), _SpecPower) * s.Specular;

MetallicSoft
使用纹理来控制反光,的软反光。
fresnel,菲涅尔反射 简单的讲,就是视线垂直于表面时,反射较弱,而当视线非垂直表面时,夹角越小,反射越明显。如果你看向一个圆球,那圆球中心的反射较弱,靠近边缘较强。

Anisotropic
拉丝钢板效果。
需要一张拉丝的法线贴图。
这里surfaceoutput是这样的:

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

经过法线贴图的修改和halfVector做点积,算出夹角。

half HdotA = dot(normalize(s.Normal + s.AnisoDirection), halfVector);float aniso = max(0, sin(radians((HdotA + _AnisoOffset) * 180.0f)));

第四章
GenerateStaticCubemap
新建一个cubemap 把readable勾选。
指的一个位置。
cs脚本,放在Editor文件夹里。

using UnityEngine;using UnityEditor;using System.Collections;public class GenerateStaticCubemap : ScriptableWizard {    public Camera  renderPosition;  // 这个可以更换成Camera,生成cubemap时可以调整如:背景色、FOV等    public Cubemap cubemap;     void OnWizardUpdate() {        // 制定public参数时会被调用,做有效性验证,设置ScriptableWizard提供的isValid变量          helpString = "Select transform to render" +              " from and cubemap to render into";          if (renderPosition != null && cubemap != null) {              isValid = true;          }          else {              isValid = false;          }      }      void OnWizardCreate() {          renderPosition.GetComponent<Camera>().RenderToCubemap(cubemap);    }      [MenuItem("CookBook/Render Cubemap")]    static void RenderCubemap() {        ScriptableWizard.DisplayWizard("Render CubeMap", typeof(GenerateStaticCubemap), "Render!");      }}

使用 IN.worldRefl 和 texCUBE 采样cubemap赋值给Emission。

o.Emission = texCUBE(_Cubemap, IN.worldRefl).rgb * _ReflAmount;

这里写图片描述

MaskedReflection
添加纹理做mask

half4 c = tex2D (_MainTex, IN.uv_MainTex);float3 reflection = texCUBE(_Cubemap, IN.worldRefl).rgb;float4 reflMask = tex2D(_ReflMask, IN.uv_MainTex);o.Albedo = c.rgb * _MainTint;o.Emission = (reflection * reflMask.r) * _ReflAmount;o.Alpha = c.a;

NormalMappedReflection
法线参与反射计算

float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;o.Normal = normals;

FresnelReflection
菲涅尔反射

half4 c = tex2D (_MainTex, IN.uv_MainTex);float rim = 1-saturate(dot(o.Normal, normalize(IN.viewDir)));rim = pow(rim, _RimPower);o.Albedo = c.rgb * _MainTint.rgb;o.Emission = (texCUBE(_Cubemap, IN.worldRefl).rgb * _ReflectionAmount) * rim;o.Specular = _SpecPower;o.Gloss = 1.0;o.Alpha = c.a;

这里写图片描述

动态cubemap
就是根据距离选择相应的cubemap(reflection probe)。
NFSnl实时环境使用了SEM(Spherical Environment Map)不是cubemap,在手机上运行效率良好。地面和车体都有使用。有空可以研究这个方案。
这里写图片描述
[ExecuteInEditMode]这个可以在编辑模式运行,是个不错的调试方法。

第五章
第六章
第七章
第八章
第九章
第十章

灰度
cs脚本,要添加成摄像机组件,指定shader,后面用的都是类似脚本只是参数不同。
2个用到的api:
http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnRenderImage.html
RenderTexture sourceTexture
这个可以通过设置camera的rect来达到控制视口的目的。
RenderTexture destTexture
这个就是camera的render target。

http://docs.unity3d.com/ScriptReference/Graphics.Blit.html

[ExecuteInEditMode] 这个添加后就可以不用运行在Game窗口中看到效果。

using UnityEngine;using System.Collections;[ExecuteInEditMode]public class TestRenderImageG : MonoBehaviour {    #region Variables    public Shader curShader;    public float grayScaleAmount = 1.0f;    private Material curMaterial;    #endregion    #region Properties    Material material{        get{            if (curMaterial == null) {                curMaterial = new Material(curShader);                curMaterial.hideFlags = HideFlags.HideAndDontSave;            }            return curMaterial;        }    }    #endregion    void Start () {        if (!SystemInfo.supportsImageEffects) {            enabled = false;            return;        }        if (!curShader && !curShader.isSupported) {            enabled = false;        }    }    void Update () {        grayScaleAmount = Mathf.Clamp( grayScaleAmount, 0.0f, 1.0f );    }    void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture) {        if (curShader != null) {            material.SetFloat("_LuminosityAmount", grayScaleAmount);            Graphics.Blit(sourceTexture, destTexture, material);        } else {            Graphics.Blit(sourceTexture, destTexture);        }    }    void OnDisable() {        if (curMaterial) {            DestroyImmediate( curMaterial );        }    }}

添加后需要指定一个cg shader,这里没有使用surface。
常见方式通过参数来调整效果百分比。
half4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);

Shader "Custom/ImageEffectG" {    Properties{        _MainTex("Main Texture",2D) = "white"{}        _LuminosityAmount ("GrayScale Amount" , Range(0.0,1)) = 1.0    }    SubShader{        Pass{            CGPROGRAM            #pragma vertex vert_img            #pragma fragment frag // 指定一个叫frag的ps            #pragma fragmentoption ARB_precision_hint_fastest // 计算数值的精确度,不用太在意            #include "UnityCG.cginc"            sampler2D _MainTex;            fixed _LuminosityAmount;            half4 frag(v2f_img i) : COLOR            {                half4 renderTex = tex2D(_MainTex, i.uv);                    float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;                half4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);                return finalColor;            }            ENDCG        }    }    Fallback "Diffuse"}

SceneDepth_Shader
深度,不知是不是unity版本问题这里采样y方向反了……
这样改了一下y方向采样:

float d = UNITY_SAMPLE_DEPTH( tex2D(_CameraDepthTexture, float2( i.uv.x, 1.0 - i.uv.y )) );

BSC_Effect
调整Brightness(亮度), saturation(饱和), contrast(对比)。
有个函数调用的例子。

BlendMode_Effect
3种混合模式:

fixed4 blendedMultiply = renderTex * blendTex;fixed4 blendedAdd = renderTex + blendTex;fixed4 blendedScreen = (1.0 - ((1.0 - renderTex) * (1.0 - blendTex)));

Overlay_Effect
根据条件判断采用的混合模式:

fixed OverlayBlendMode(fixed basePixel, fixed blendPixel){    if(basePixel < 0.5)    {        return (2.0 * basePixel * blendPixel);    }    else    {        return (1.0 - 2.0 * (1.0 - basePixel) * (1.0 - blendPixel));    }}

第十一章
2个比较复杂的ps效果,很好的参考。
OldFilmEffectShader
UV动画中使用,_SinTime 和 _RandomValue。
NightVisionEffectShader
这里有个透镜变形的算法。

0 0