/* 声明一个shader 命名ZSSS 存于Custom , 可以改 , 文件名和里面写的shader地址和名字可以不同*/ /* 这个shader刚创建的时候只有54行 => unity版本:2019.4.18f1*/ Shader "Custom/ZSSS" { /* 在将第一块内容之前,请先去了解一下UV纹理。简单来说:A是一张图片(称A为纹理),B是一张坐标信息(称B为UV),用B来取A就是纹理贴图的精髓了,举个简单的例子: A的色彩如下:(这个图很小 只有四个像素点) 红 黄 蓝 绿 B的坐标信息如下: (0,0) (1,1) (1,1) (0,1) 那么取出来得到的纹理贴图就是: 红 绿 绿 黄 这样子,是不是很无聊?当这上面的点数达到很大的量级时,就很有意义了。 还没完,你还需要了解镜面高光和金属度的概念,自行百度(或者先跳过这个知识点继续往下看)。 */ /*镜面高光和金属度*/ /*Properties 属性声明 */ Properties { _Color ("Color", Color) = (1,1,1,1) /* 用_XX 等形式声明的变量就是属性 , 这句的语法意义是: * _XXX (展示名,数据类型) = 默认值 * 展示名是在untiy中可见的名称 , Color 其实是一种数据类型类似int的 * Color 一种颜色 由RGBA(红黄蓝透)四个维度定义 * 2D 一张贴图 横纵大小为2的幂次 ,这张图将在采样后转为对应着基于模型uv的每个像素的颜色的值 最终显示于屏幕 * Rect 一张贴图 横纵大小与2的幂次无关 * Cube 立方体纹理(Cube map texture),6张有关联的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样 * Range(min,max) 一个介于最小和最大值之间的浮点数 , 一般用来调整shader某些特性的参数(比如透明度渲染截止值可以是从0到1的) * Float 任意一个浮点数 * Vector 四维数 写作(x,y,z,w) * * defaultValue 定义了这个属性的默认值 通过输入一个符合格式的默认值来指定对应属性的初始值(某些效果可能需要特定的参数值来达到需要的效果 , 虽然这些值可以在之后进行调整,但是如果默认值就是想要的效果就会精简一个调整的步骤) */ _MainTex ("Albedo (RGB)", 2D) = "white" {} /* 这里表示一张贴图,这个贴图的默认值 在这里用空白表示*/ _Glossiness ("Smoothness", Range(0,1)) = 0.618 /* 高光设置 0.5 ? [Glossiness;光泽度 反光度 ; Smoothness:平滑度] Glossiness (缩写Gloss) Glossiness定义材质得粗糙度信息,跟Roughness相反,0 (黑色-0 sRGB) 表示粗糙,1 (白-255 sRGB) 表示光滑。 光泽度无非是指表面反射光线的能力。 表面能够反射的光线越多,光泽度越高。 表面能够反射的光线越少,光泽度越低。 表面反射光线的能力受环境中各种因素的影响, 例如落在对象上的那些非常小颗粒的灰尘,以及接触对象时从手上沾染到对象上的油污, 所有这一切都会影响表面反射光线的能力。 Smoothness (平滑度) 在Unity中,用**Smoothness (平滑度) **来定义材质的粗糙或平滑度,并具有不同的镜面反射级别和颜色。 平滑度越底,材质越粗超 (漫射) ;平滑度越高,材质越光滑 (镜面反射) 。 在Unity中,Smoothness (平滑度) 的取值跟Roughness (粗糙度) 的取值相反 (把贴图反向) ,但都是表示材质表面的粗糙程度。 在Unity中,Smoothness (平滑度) 的概念同时适用于镜面反射 (Specular) 工作流程和金属性 (Metallic) 工作流程,并且在两者中的工作方式非常相同。 默认情况下,如果未分配 Metallic 或 Specular 纹理映射,则材质的平滑度由滑动条控制。此滑动条可用于控制表面上的“微表面细节”或平滑度。 在 Unity 中,“微表面细节”不是直接可见的。它是光照计算中使用的概念。但是,你可以看到这个微表面细节的效果,它表示当光线从对象反弹时散射的光量。 在光滑的表面上,所有光线都倾向于以可预测和一致的角度反弹。在极端的情况下,一个完美光滑表面的光反射就像镜子一样。 较不光滑的表面会在较宽的角度范围内反光 (当光照射到微表面的凸起时) ,因此反射具有较少的细节并以更倾向于漫射的方式在表面上扩散。 Roughness (粗糙度) Roughness是一个属性 (反射属性) ,控制材质表面的粗糙或平滑程度 (控制材质反射光的强度) 。与平滑的材质相比,粗糙的材质将向更多方向散射所反射的光线。 这决定了反射的模糊或清晰度 (或者镜面反射高光的广度或密集度) 。在UE4中是用Roughness来控制材质的粗糙度。 在Roughness贴图中,0 (黑色-0 sRGB) 表示光滑,1 (白-255 sRGB) 表示粗糙。粗糙度在帮助确定表面光泽度方面扮演着重要角色。 表面越粗糙,其光泽度越低。 表面越光滑,其光泽度越高 */ _Metallic ("Metallic", Range(0,1)) = 0.382 /* 金属度 ? 材质中的metallic参数决定了物体表面看起来像不像金属。但表面越是偏于金属,环境的反射则越强,对应的albedo颜色就越弱。 当把metallic参数设置满,即金属性超高的表面颜色将完全受环境内容驱动。当金属性越低 (metallic参数越小) ,albedo颜色将越清晰。 金属度 (Metallic) 控制材质的金属性,如控制材质表面在多大程度上"像金属"。 金属度贴图起到类似于蒙版的作用,区分固有色贴图中的金属和绝缘体数据。 在金属性贴图中,金属的金属度 (Metallic) 值为 1,非金属的金属度 (Metallic) 值为 0。 对于纯表面,例如纯金属、纯石头、 纯塑料等等,金属度值将是 0 或 1,而不是任何介于它们之间的值。 创建受腐蚀、落满灰尘或生锈金属之类的混合表面时,金属度值介于 0 与 1 之间。 金属感对象的光泽度由粗糙度控制。 材质越粗糙,其光泽度就会越低, 而缺少粗糙度将使金属显得非常有光泽 */ } /* 什么是 SubShader , 如果你用过ue4 就很容易理解,对应着UE4中的材质表达式 , UE4中的材质表达式或者Unity中的shader本质是什么呢 ?: 简单来说 就是将图片通过处理 得到另一张图片( 比如用图片+uv信息=>纹理贴图 的过程) , SubShader 定义了图片处理过程*/ SubShader { /*Tags 规定了混合模型(ue4中的概念) [混合模式决定了材质和背景的颜色混合方式]: Translucent: opaque*材质底色 + (1-opaque)*背景色 Additice: 材质底色 + 背景色 Modulate: 材质底色 * 背景色 Masked: (Masked?材质底色:背景色) */ /*Unity中RenderType的所有类型: Opaque: 用于大多数着色器(法线着色器、自发光着色器、反射着色器以及地形的着色器)。 Transparent:用于半透明着色器(透明着色器、粒子着色器、字体着色器、地形额外通道的着色器)。 TransparentCutout: 蒙皮透明着色器(Transparent Cutout,两个通道的植被着色器)。 Background: Skybox shaders. 天空盒着色器。 Overlay: GUITexture, Halo, Flare shaders. 光晕着色器、闪光着色器。 TreeOpaque: terrain engine tree bark. 地形引擎中的树皮。 TreeTransparentCutout: terrain engine tree leaves. 地形引擎中的树叶。 TreeBillboard: terrain engine billboarded trees. 地形引擎中的广告牌树。 Grass: terrain engine grass. 地形引擎中的草。 GrassBillboard: terrain engine billboarded grass. 地形引擎何中的广告牌草 */ /*表面着色器可以被若干标签所修饰 , 之后 硬件将通过判定这些标签来决定什么时候调用这个着色器 比如下面这句 告诉系统应该在渲染非透明物体时调用 Unity定义了一系列这样的渲染选项*/ Tags { "RenderType" = "Opaque" } /*除了RenderType之外 另外的标签还有"IgnoreProjector"="True"(不被Projectors影响) , "ForceNoShadowCasting"="True" (从不产生阴影) "Queue"="xxx"(指定渲染顺序队列) 这里想要着重说一下的是Queue这个标签,如果你使用Unity做过一些透明和不透明物体的混合的话, 很可能已经遇到过不透明物体无法呈现在透明物体之后的情况。这种情况很可能是由于Shader的渲染顺序不正确导致的。 Queue指定了物体的渲染顺序,预定义的Queue有: Background - 最早被调用的渲染,用来渲染天空盒或者背景 Geometry - 这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的) AlphaTest - 用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑 Transparent - 以从后往前的顺序渲染透明物体 Overlay - 用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效) 这些预定义的值本质上是一组定义整数, Background = 1000, Geometry = 2000, AlphaTest = 2450, Transparent = 3000, Overlay = 4000。在我们实际设置Queue值时,不仅能使用上面的几个预定义值,我们也可以指定自己的Queue值,写成类似这样: "Queue"="Transparent+100",表示一个在Transparent之后100的Queue上进行调用。通过调整Queue值, 我们可以确保某些物体一定在另一些物体之前或者之后渲染,这个技巧有时候很有用处 */ /*----------------------------------------------------------------------------------------------------------------------------------------*/ /* LOD很简单,它是Level of Detail的缩写,在这里例子里我们指定了其为200(其实这是Unity的内建Diffuse着色器的设定值)。 这个数值决定了我们能用什么样的Shader。在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时, 这个SubShader将不可用。Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值, 这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。 VertexLit及其系列 = 100 Decal, Reflective VertexLit = 150 Diffuse = 200 Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250 Bumped, Specular = 300 Bumped Specular = 400 Parallax = 500 Parallax Specular = 600 */ LOD 200 /* ↓ 前面杂项说完了,终于可以开始看看最主要的部分了,也就是将输入转变为输出的代码部分 ↓↓ */ /* CGPROGRAM。这是一个开始标记,表明从这里开始是一段CG程序(我们在写Unity的Shader时用的是Cg/HLSL语言) * C for graphics 最后一行的ENDCG与CGPROGRAM是对应的,表明CG程序到此结束。 */ CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types[基于物理的标准照明模型,并在所有灯光类型上启用阴影] //接下来是是一个编译指令: /**它声明了我们要写一个表面Shader,并指定了光照模型。它的写法是这样的 * #pragma surface surfaceFunction lightModel [optionalparams] surface - 声明的是一个表面着色器 surfaceFunction - 着色器代码的方法的名字 lightModel - 使用的光照模型。 所以在我们的例子中,我们声明了一个表面着色器,实际的代码在surf函数中(在下面能找到该函数),使用Lambert(也就是普通的diffuse)作为光照模型。 */ #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting [使用着色器模型3.0目标,以获得更好的照明效果] #pragma target 3.0 /* sampler2D是个啥?其实在CG中,sampler2D就是和texture所绑定的一个数据容器接口。 等等..这个说法还是太复杂了,简单理解的话,所谓加载以后的texture(贴图)说白了不过是一块内存存储的, 使用了RGB(也许还有A)通道,且每个通道8bits的数据。而具体地想知道像素与坐标的对应关系,以及获取这些数据, 我们总不能一次一次去自己计算内存地址或者偏移,因此可以通过sampler2D来对贴图进行操作。 更简单地理解,sampler2D就是GLSL中的2D贴图的类型,相应的,还有sampler1D,sampler3D,samplerCube等等格式。 //------------------------------ 解释通了sampler2D是什么之后,还需要解释下: 为什么在这里需要一句对 [ _MainTex ] 的声明,之前我们不是已经在 { Properties } 里声明过它是贴图了么。 答案是我们用来实例的这个shader其实是由两个相对独立的块组成的, 外层的属性声明,回滚等等是Unity可以直接使用和编译的ShaderLab; 而现在我们是在 CGPROGRAM...ENDCG 这样一个代码块中,这是一段CG程序。对于这段CG程序,要想访问在 { Properties } 中所定义的变量的话,必须使用和之前变量相同的名字进行声明。 于是其实 sampler2D _MainTex; 做的事情就是 再次声明并链接了 { Properties } 中的_MainTex ,使得接下来的CG程序能够使用这个变量。 */ sampler2D _MainTex; /* 终于可以继续了。接下来是一个struct结构体。相信大家对于结构体已经很熟悉了,我们先跳过之,直接看下面的的surf函数。 上面的#pragma段已经指出了我们的着色器代码的方法的名字叫做surf,那没跑儿了,就是这段代码是我们的着色器的工作核心。我们已经说过不止一次, 着色器就是给定了输入,然后给出输出进行着色的代码。 CG规定了声明为表面着色器的方法(就是我们这里的surf)的参数类型和名字,因此我们没有权利决定surf的输入输出参数的类型,只能按照规定写。 这个规定就是第一个参数是一个Input结构,第二个参数是一个inout的SurfaceOutput结构。 它们分别是什么呢?Input其实是需要我们去定义的结构,这给我们提供了一个机会,可以把所需要参与计算的数据都放到这个Input结构中,传入surf函数使用; SurfaceOutput是已经定义好了里面类型输出结构,但是一开始的时候内容暂时是空白的,我们需要向里面填写输出,这样就可以完成着色了。 先仔细看看INPUT吧,现在可以跳回来看上面定义的INPUT结构体了: */ /* 作为输入的结构体必须命名为Input, 这个结构体中定义了一个float2的变量…你没看错我也没打错,就是float2,表示浮点数的float后面紧跟一个数字2,这又是什么意思呢?其实没什么魔法, float和vec都可以在之后加入一个2到4的数字,来表示被打包在一起的2到4个同类型数。比如下面的这些定义: //Define a 2d vector variable vec2 coordinate; //Define a color variable float4 color; //Multiply out a color float3 multipliedColor = color.rgb * coordinate.x; 在访问这些值时,我们即可以只使用名称来获得整组值,也可以使用下标的方式(比如.xyzw,.rgba或它们的部分比如.x等等)来获得某个值。 在这个例子里,我们声明了一个叫做 [ uv_MainTex ] 的包含两个浮点数的变量。 如果你对3D开发稍有耳闻的话,一定不会对uv这两个字母感到陌生。 UV mapping的作用是将一个2D贴图上的点按照一定规则映射到3D模型上,是3D渲染中最常见的一种顶点处理手段。 在CG程序中,我们有这样的约定,在一个贴图变量(在我们例子中是 [ _MainTex ] )之前加上uv两个字母, 就代表提取它的uv值(其实就是两个代表贴图上点的二维坐标) 我们之后就可以在surf程序中直接通过访问uv_MainTex来取得这张贴图当前需要计算的点的坐标值了 */ struct Input { float2 uv_MainTex; }; /*如果你坚持看到这里了,那要恭喜你,因为离最后成功读完一个Shader只有一步之遥。 我们回到surf函数,它的两有参数, 第一个是Input,我们已经明白了:在计算输出时Shader会多次调用surf函数,每次给入一个贴图上的点坐标,来计算输出。 第二个参数是一个可写的SurfaceOutput,SurfaceOutput是预定义的输出结构,我们的surf函数的目标就是根据输入把这个输出结构填上。 SurfaceOutput结构体的定义如下(表面着色器的标准输出结构): struct SurfaceOutput { fixed3 Albedo; // 漫射颜色 fixed3 Normal; // 切线空间法线 (如果已写入) fixed3 Emission; half Specular; // 0..1 范围内的镜面反射能力 fixed Gloss; // 镜面反射强度 fixed Alpha; // 透明度 Alpha }; 在 Unity 5 中,表面着色器还可以使用基于物理的光照模型。内置标准光照模型和标准镜面反射光照模型 (见下文) 分别使用以下输出结构: struct SurfaceOutputStandard { fixed3 Albedo; // 基础 (漫射或镜面反射) 颜色 fixed3 Normal; // 切线空间法线 (如果已写入) half3 Emission; half Metallic; // 0=非金属,1=金属 half Smoothness; // 0=粗糙,1=平滑 half Occlusion; // 遮挡 (默认为 1) fixed Alpha; // 透明度 Alpha }; struct SurfaceOutputStandardSpecular { fixed3 Albedo; // 漫射颜色 fixed3 Specular; // 镜面反射颜色 fixed3 Normal; // 切线空间法线 (如果已写入) half3 Emission; half Smoothness; // 0=粗糙,1=平滑 half Occlusion; // 遮挡 (默认为 1) fixed Alpha; // 透明度 Alpha }; 这里的half和我们常见float与double类似,都表示浮点数,只不过精度不一样。也许你很熟悉单精度浮点数(float或者single)和双精度浮点数(double), 这里的half指的是半精度浮点数,精度最低,运算性能相对比高精度浮点数高一些,因此被大量使用。 在例子中,我们做的事情非常简单: half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; 这里用到了一个 [ tex2d ] 函数,这是CG程序中用来在一张贴图中对一个点进行采样的方法,返回一个float4。 这里对_MainTex在输入点上进行了采样,并将其颜色的rbg值赋予了输出的像素颜色,将a值赋予透明度。 于是,着色器就明白了应当怎样工作:即找到贴图上对应的uv点,直接使用颜色信息来进行着色 fixed4 类似float4 但是精度较低 /----------------------------- CG支持7种数据类型: { float 32位浮点数 half 16位浮点数 int 32位整形数 fixed 12位定点数 bool 布尔数据 sampler 纹理对象的句柄 共有:sampler、sampler1D、sampler2D、sampler3D、samplerCUBE、和samplerRECT六种。 string 字符串,其实没有必要在CG中用到字符串 } 此外,CG还支持矩阵数据类型 如: float2×4 matrix; //表示2×4阶矩阵,包含8个float类型数据 类型使用技巧: 1.精度够用就好 2.颜色和单位向量,使用fixed 3.其他情况,尽量使用half(即范围在[-6万,+6万]、精确的小数点3.3位);否则才使用用float //------------------------------- */ half _Glossiness; //必须使用和之前变量相同的名字进行声明! half _Metallic; //必须使用和之前变量相同的名字进行声明! fixed4 _Color; //必须使用和之前变量相同的名字进行声明! // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing. // #pragma instancing_options assumeuniformscaling /* 这是一组宏: #pragma multi_compile_instancing 用于命令 Unity 生成实例化变体。对于表面着色器来说是不需要的。 UNITY_VERTEX_INPUT_INSTANCE_ID 用于在顶点着色器输入/输出结构中定义实例 ID。请参阅 SV_InstanceID 以了解更多信息。 UNITY_INSTANCING_BUFFER_START(name) 必须在特殊命名的常量缓冲区中定义每个实例的属性。使用这对宏来包装对每个实例唯一的属性。 UNITY_INSTANCING_BUFFER_END(name) 同上 UNITY_DEFINE_INSTANCED_PROP(float4, _Color) 用于根据类型和名称定义每个实例着色器的属性。在此示例中,_Color 属性是唯一的。 UNITY_SETUP_INSTANCE_ID(v); 用于使着色器函数可以访问实例 ID。它必须在顶点着色器的最开头使用,并且对于片元着色器是可选的。 UNITY_TRANSFER_INSTANCE_ID(v, o); 用于将实例 ID 从顶点着色器的输入结构体复制到输出结构体中。仅当需要访问片元着色器中的每个实例的数据时才有必要这样做。 UNITY_ACCESS_INSTANCED_PROP(arrayName, color) 用于访问在实例化常量缓冲区中声明的每个实例着色器的属性。它使用实例 ID 来索引到实例数据数组。 该宏中的 arrayName 必须与 UNITY_INSTANCING_BUFFER_END(name) 宏中的匹配。 在本例中: 使用 UNITY_INSTANCING_BUFFER_START 宏宣告要使用GPU多例化技术的变量, 使用 UNITY_INSTANCING_BUFFER_END 宏结束使用技术的宣告。 */ UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here // 啥也没做 浪费了 UNITY_INSTANCING_BUFFER_END(Props) /*核心的处理函数:surf,输入一张二维浮点信息,也即是上面的uv_MainTex, 输出一个o表示材质 就是上面提到的 struct SurfaceOutput (inout像c++里面的按照引入传入,虽说没有返回,但是也有信息传出的效果) */ void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color [反照率来自于被颜色着色的纹理] /*看这里:tex2D就是前面说的"利用UV去取图片获得纹理贴图"的做法,然后再乘以_Color, 这里用到了乘法的颜色算法,自己体会(这个_Color默认值为1,1,1,1,所以默认下不影响) 到这里可以知道:fixed4表示一种四维矢量(用来表示颜色就挺合适的。此外,它是定点型小数而非浮点型小数) */ fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; /* 下面是对输出参数o做的相关设置: 其他没有设置的就是用默认值 这里只是设置了它的 Albedo 基础 (漫射或镜面反射) 颜色 Metallic 0=非金属 1=金属 Smoothness 0=粗糙,1=平滑 Alpha 透明度 */ o.Albedo = c.rgb ; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } /*最后一行的ENDCG与CGPROGRAM是对应的,表明CG程序到此结束。*/ ENDCG } /* 到此为止shader需要编译的部分结束,下面这句是个保险,如果上面自己写的那些东西在运行的机器上不支持 则用下面的方式替换渲染 这个是最简单最基础的shader一定会被所有机器支持的那种*/ FallBack "Diffuse" } /***** Shader程序的基本结构 , 一个普通的着色器: ┍┄┄┄┄┄┑ ┆ 着色器 ┆ ┆ shader ┆ ┕┄┄┄┄┄┙ │ ┍┄┄┄┄┄┄┑ │ ┆ 属性定义 ┆ ├───▶ ┆ Property ┆ │ ┆ Definition ┆ │ ┕┄┄┄┄┄┄┙ │ ┍┄┄┄┄┄┄┑ │ ┆ 子着色器 ┆ │ ┆ SubShader ┆ │ ┕┄┄┄┄┄┄┙ │ │ ┍┄┄┄┄┄┄┑ │ ├────▶┆ Pass1 ┆ │ │ ┆ ┆ │ │ ┕┄┄┄┄┄┄┙ │ │ ↓ │ │ ┍┄┄┄┄┄┄┑ │ ┕────▶┆ Pass2 ┆ │ ┆ ┆ │ ┕┄┄┄┄┄┄┙ │ ┆ │ ↓ │ ...... │ │ ┍┄┄┄┄┄┄┑ ├────▶┆ 子着色器 ┆ │ ┆ SubShader ┆ │ ┕┄┄┄┄┄┄┙ │ │ ┍┄┄┄┄┄┄┑ ┕────▶┆ 回归 ┆ ┆ FailBack ┆ ┕┄┄┄┄┄┄┙ */ /** 参考文献: * https://www.cnblogs.com/JackSamuel/p/7219932.html * http://www.360doc.com/content/13/0923/15/12282510_316492286.shtml * https://blog.csdn.net/a133900029/article/details/80558332 * https://docs.unity3d.com/cn/current/Manual/SL-SurfaceShaders.html * https://docs.unity3d.com/cn/current/Manual/GPUInstancing.html * https://blog.csdn.net/u012204304/article/details/107691901 */