Shader开发之三大着色器

固定功能管线着色器Fixed Function Shaders

固定功能管线着色器的关键代码一般都在Pass的材质设置Material{}和纹理设置SetTexture{}部分。

Shader "Custom/VertexList" {

    Properties {

        _Color("Main Color",Color) = (0,1,1,0.5)

        _SpecColor("Spec Color",Color) = (1,1,1,1)

        _Emission("Emissive Color",Color) = (0,0,0,0)

        _Shininess("Shininess",Range(0.01,1)) = 0.7

        _MainTex ("Base (RGB)", 2D) = "white" {}

    }

    SubShader {

        Pass{

                Material{

                   Diffuse[_Color]

                   Ambient[_Color]

                   Shininess[_Shininess]

                   Specular[_SpecColor]

                   Emission[_Emission]

                }      

                Lighting On

                SeparateSpecular On//启用高光颜色

                //设置纹理

                SetTexture[_MainTex]{

                   //设置颜色常量

                   constantColor[_Color]

                   //混合命令

                   combine texture * primary DOUBLE,

                   texture *constant

                }

            }

    }

}

表面着色器Surface Shaders

在Unity中,表面着色器的关键代码用Cg/HLSL语言编写,然后嵌在ShaderLab的结构代码中使用。使用表面着色器,用户仅需要编写最关键的表面函数,其余周边代码将由Unity自动生成,包括适配各种光源类型、渲染实时阴影以及集成到前向/延迟渲染管线中等。

编写表面着色器有几个规则:

1)表面着色器的实现代码需要放在CGPROGRAM..ENDCG代码块中,而不是Pass结构中,它会自己编译到各个Pass。

2)使用#pragma surface..命令来指明它是一个表面着色器。

#pragma surface 表面函数光照模型[可选参数]

其中表面函数用来说明哪个Cg函数包含有表面着色器代码,表面函数的形式为:

void surf(Input IN,inoutSurfaceOutPut 0)

光照模型可以是内置的Lambert和BlinnPhong,或者是自定义的光照模型。

表面函数的作用是接收输入的UV或者附加数据,然后进行处理,最后将结构填充到输出结构体SurfaceOutPut中。

输入结构体Input一般包含着色器所需的纹理坐标,纹理坐标的命名规则为uv加纹理名称。另外还可以在输入结构中设置一些附加数据:

SurfaceOut描述了表面的各种参数,它的标准结构为:

struct SurfaceOutput{
    half3 Albedo;//反射光
    half3 Normal;//法线
    half3 Emission;//自发光
    half Specular;//高光
    half Gloss;//光泽度
    half Alpha;//透明度
};

将输入数据处理完毕后,将结果填充到输出结构体中。

相关示例:

1、使用内置的Lambert光照模型,并设置表面颜色为白色。

 1 Shader "Custom/Diffuse Simple" {
 2
 3     SubShader{
 4
 5         Tags{"RenderType" = "Opaque"}
 6
 7         CGPROGRAM//表面着色器的实现代码
 8
 9         //指明着色器类型,表面函数和光照模型
10
11         #pragma surface surf Lambert
12
13
14
15         struct Input{//输入的数据结构体
16
17             float color : COLOR;
18
19         };
20
21
22
23         void surf(Input IN,inoutSurfaceOutput o){//表面函数
24
25             o.Albedo = 1;//输出颜色值
26
27         }
28
29         ENDCG;
30
31
32
33     }
34
35     Fallback "Diffuse"//备选着色器
36
37 }

渲染效果如下:

2、在示例1的基础上添加纹理,代码如下:

 1 Shader "Custom/Diffuse Simple" {
 2
 3
 4
 5     Properties{//添加纹理属性
 6
 7         _MainTex("Texture",2D) = "White"{}
 8
 9
10
11     }
12
13     SubShader{
14
15         Tags{"RenderType" = "Opaque"}
16
17         CGPROGRAM   //表面着色器的实现代码
18
19         //指明着色器类型,表面函数和光照模型
20
21         #pragma surface surf Lambert
22
23
24
25         struct Input{//输入的数据结构体
26
27             float2 uv_MainTex;
28
29         };
30
31
32
33         sampler2D _MainTex;
34
35
36
37         void surf(Input IN,inout SurfaceOutput o){//表面函数
38
39             o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
40
41         }
42
43         ENDCG
44
45
46
47     }
48
49     Fallback "Diffuse"//备选着色器
50
51 }

渲染效果如下:

3、在示例2的基础上添加法线贴图

 1 Shader "Custom/Diffuse Simple"{
 2
 3     Properties{//添加纹理属性
 4
 5         _MainTex("Texture",2D) = "white"{}
 6
 7         //添加法线贴图属性
 8
 9         _BumpMap("Bumpmap",2d) = "bump"
10
11     }
12
13     SubShader{
14
15         Tags{"RenderType" = "Opaque"}
16
17         CGPROGRAM//表面着色器的实现代码
18
19         //指明着色器类型,表面函数和光照模型
20
21         #pragma surface surf Lambert
22
23         struct Input{//输入的数据结构体
24
25             float2 uv_MainTex;
26
27             float2 uv_BumpMap;
28
29         };
30
31         sampler2D _MainTex;
32
33         sampler2D _BumpMap;
34
35         void surf(Input IN,inout SurfaceOutput o){//表面函数
36
37             o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
38
39             o.Normal = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));
40
41         }
42
43         ENDCG
44
45     }
46
47     Fallback "Diffuse"//备选着色器
48
49 }

渲染效果如下:

4、添加立方体贴图反射

 1 Shader "Surface Shader Example/Diffuse Simple"{
 2
 3     Properties{//添加纹理属性
 4
 5     _MainTex("Texture",2D) = "white"{}
 6
 7     //立方体贴图属性
 8
 9     _Cube("Cubemap",CUBE) = ""{}
10
11 }
12
13     SubShader{
14
15             Tags{"RenderType" = "Opaque"}
16
17             CGPROGRAM//表面着色器的实现代码
18
19             //指明着色器类型,表面函数和光照模型
20
21             #pragma surface surf Lambert
22
23             struct Input{//输入的数据结构体
24
25                 float2 uv_MainTex;
26
27                 float3 worldRefl;//输入反射参数
28
29         };
30
31             sampler2D _MainTex;
32
33             SamplerCUBE _Cube;
34
35             void surf(Input IN,inout SurfaceOutput o){//表面函数
36
37                 o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
38
39                 //将发射颜色设置给自发光颜色
40
41                 o.Emission = texCUBE(_Cube,IN.worldRefl).rgb;
42
43             }
44
45             ENDCG
46
47     }
48
49     Fallback "Diffuse"//备选着色器
50
51 }

渲染效果如下:

顶点片段着色器Vertex And Fragment Shaders

顶点片段着色器运行于具有可编程渲染管线的硬件上,它包括顶点程序Vertex Programs和片段程序Fragment Programs。当在使用顶点程序或片段程序进行渲染的时候,图形硬件的固定功能管线会关闭,具体来说就是编写的顶点程序会替换掉固定管线中标准的3D变换,光照,纹理坐标生成等功能,而片段程序会替换掉SetTexture命令中的纹理混合模式。因此编写顶点片段着色器需要对3D变化,光照计算等有非常透彻的了解,需要写代码来替代D3D或者OpenGL原先在固定功能管线中要做的工作。

与表面着色器一样,顶点片段着色器也需要用Cg/HLSL来编写核心的实现代码,代码用CGPROGRAM ENDCG语句包围起来,放在ShaderLab的Pass命令中,形式如下:

Pass{
//通道设置
CGPROGRAM
//本段Cg代码的编译命令
#pragma vertexvert
#pragma fragment frag
//Cg代码
ENDCG
//其他通道设置
}

顶点着色程序从GPU前端模块(寄存器)中提取图元信息(顶点位置、法向量、纹理坐标等),并完成顶点坐标空间转换、法向量空间转换、光照计算等操作,最后将计算好的数据传送到指定寄存器中;然后片断着色程序从中获取需要的数据,通常为“纹理坐标、光照信息等”,并根据这些信息以及从应用程序传递的纹理信息(如果有的话)进行每个片断的颜色计算,最后将处理后的数据送光栅操作模块。

顶点着色器和像素着色器的数据处理流程

在应用程序中设定的图元信息(顶点位置坐标、颜色、纹理坐标等)传递到vertex buffer中;纹理信息传递到texture buffer中。其中虚线表示目前还没有实现的数据传递。当前的顶点程序还不能处理纹理信息,纹理信息只能在片断程序中读入。

顶点着色程序与片断着色程序通常是同时存在,相互配合,前者的输出作为后者的输入。不过,也可以只有顶点着色程序。如果只有顶点着色程序,那么只对输入的顶点进行操作,而顶点内部的点则按照硬件默认的方式自动插值。例如,输入一个三角面片,顶点着色程序对其进行phong光照计算,只计算三个顶点的光照颜色,而三角面片内部点的颜色按照硬件默认的算法(Gourand明暗处理或者快速phong明暗处理)进行插值,如果图形硬件比较先进,默认的处理算法较好(快速phong明暗处理),则效果也会较好;如果图形硬件使用Gourand明暗处理算法,则会出现马赫带效应(条带化)。

而片断着色程序是对每个片断进行独立的颜色计算,并且算法由自己编写,不但可控性好,而且可以达到更好的效果。

由于GPU对数据进行并行处理,所以每个数据都会执行一次shader程序程序。即,每个顶点数据都会执行一次顶点程序;每个片段都会执行一次片段程序。

片段就是所有三维顶点在光栅化之后的数据集合,这些数据没有经过深度值比较,而屏幕显示的像素是经过深度比较的。

顶点片段着色器中编译命令的一些说明:

编译命令Compilation directive

编译目标Shader targets

下面是一些顶点片段着色器的例子:

1)根据发现方向设置模型表面颜色

 1 Shader"Tutorial/Display Normals"{
 2
 3     SubShader{
 4
 5         Pass{
 6
 7             CGPROGRAM
 8
 9             //CG代码块开始
10
11             #pragma vertex vert
12
13             #pragma fragment frag
14
15             #include "UnityCG.cginc"
16
17             struct v2f{
18
19                 float4 pos:SV_POSITION;
20
21                 float3 color:COLOR0;
22
23             };
24
25                v2f vert(appdata_base v)
26
27                {//顶点程序代码,计算位置和颜色
28
29                   v2f o;
30
31                   o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
32
33                   o.color = v.normal*0.5+0.5;
34
35                   return o;
36
37                }
38
39               half4 frag(v2f i):COLOR
40
41              {
42
43                  //片段程序代码,直接把输入的颜色返回,并把透明度设置为1
44
45                    return half(i.color,1);
46
47              }
48
49             ENDCG
50
51         }
52
53     }
54
55     Fallback "VertexLit"
56
57 }

渲染效果如下:

2)根据切线方向设置模型表面颜色。

 1 Shader"vertex and fragment example/Tangents"{
 2
 3     SubShader{
 4
 5         Pass{
 6
 7             Fog{Mode Off}
 8             CGPROGRAM
 9
10             //CG代码块开始
11
12             #pragma vertex vert
13
14             #pragma fragment frag
15
16             //输入位置和切线数据
17
18             struct appdata{
19
20                 float4 vertex : POSITION;
21
22                 float4 tangent : TANGENT;
23
24             };
25
26             struct v2f{
27                 float4 pos : POSITION;
28                 fixed4 color : COLOR;
29
30             };
31                v2f vert(appdata_base v)
32
33                {//顶点程序代码,计算位置和颜色
34
35                   v2f o;
36
37                   o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
38
39                   o.color = v.tangent*0.5+0.5;
40
41                   return o;
42
43                }
44
45               fixed4 frag(v2f i):COLOR0
46               {
47
48                  //片段程序代码,直接把输入的颜色返回,并把透明度设置为1
49
50                     return i.color;
51
52               }
53
54              ENDCG
55
56         }
57
58     }
59
60 }

渲染效果如下:

时间: 2024-10-05 18:41:49

Shader开发之三大着色器的相关文章

(译)Minimal Shader(最小的着色器)

(原文:https://en.wikibooks.org/wiki/Cg_Programming/Unity/Minimal_Shader) This tutorial covers the basic steps to create a minimal Cg shader in Unity. 本节课包含了在Unity中创建一个最小的Cg着色器的基本步骤. Starting Unity and Creating a New Project(打开Unity创建一个新工程) After downlo

OpenGL 4.0的Tessellation Shader(细分曲面着色器)

细分曲面着色器(Tessellation Shader)处于顶点着色器阶段的下一个阶段,我们可以看以下链接的OpenGL渲染流水线的图:https://www.opengl.org/wiki/Rendering_Pipeline_Overview(可能需要FQ). 细分曲面着色器 直到这个阶段,对于操作几何图元而言,只有顶点着色器对我们可用.尽管使用顶点着色器可以使用不少图形技术,不过顶点着色器也确实存在一些限制.一个就是它们在执行过程中无法创建不能创建额外的几何图形.它们仅仅更新与它们当前所处

Unity3D的Shader基本结构—子着色器SubShader

一.子着色器SubShader 写法:SubShader{ Tags{ "Queue"="Transparent" } LOD 100 } 一.标签(Tags) 1.”Queue”标签.定义渲染顺序.预制的值为 (1)”Background”.值为1000.比如用于天空盒. (2)”Geometry”.值为2000.大部分物体在这个队列.不透明的物体也在这里.这个队列内部的物体的渲染顺序会有进一步的优化(应该是从近到远,early-z test可以剔除不需经过FS处

Unity3d之Shader编程:子着色器、通道与标签的写法 & 纹理混合

一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器定义了一个渲染通道的列表,并可选是否为所有通道初始化所需要的通用状态.子着色器的写法如下: Subshader{ [Tags] [CommonState] Passdef [Passdef ...] } 也就是通过可选标签,通用状态 和 一个Pass 定义的列表构成了子着色器. 当Unity选择用于

WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码

原文:WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码 HLSL,High Level Shader Language,高级着色器语言,是 Direct3D 着色器模型所必须的语言.WPF 支持 Direct3D 9,也支持使用 HLSL 来编写着色器.你可以使用任何一款编辑器来编写 HLSL,但 Shazzam Shader Editor 则是专门为 WPF 实现像素着色器而设计的一款编辑器,使用它来编写像素着色器,可以省去像素着色器接入

GPU渲染管线与可编程着色器

本文由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/71978861 这篇文章是解析计算机图形学界"九阴真经总纲"一般存在的<Real-Time Rendering 3rd>系列文章的第三篇.将带来RTR3第三章内容"Chapter 3 The Graphics Processing Unit 图形处理器"的总结.概括与提炼. 这章的主要内容是介绍G

【OPENGL】第三章 着色器基础(一)

在这一章,我们会学习什么是着色器(Shader),什么是着色器语言(OpenGL Shading Language-GLSL),以及着色器怎么和OpenGL程序交互. 首先我们先来看看什么叫着色器. Shader(着色器)是用来实现图像渲染的,用来替代固定渲染管线的可编程程序. 着色器替代了传统的固定渲染管线,可以实现3D图形学计算中的相关计算,由于其可编程性,可以实现各种各样的图像效果而不用受显卡的固定渲染管线限制.这极大的提高了图像的画质. 在上一篇文章( http://www.cnblog

OpenGL ES着色器语言之着色概览(官方文档)

OpenGL ES着色器语言之着色概览(官方文档第二章) 事实上,OpenGL ES着色语言是两种紧密关联的语言.这些语言用来在OpenGL ES处理管线的可编程处理器创建着色器. 在本文档中,除非另外说明,一个语言功能适用于所有语言,并且通用用法将把他们当做一个语言来看待.特定语言将指出它们的目标处理器:顶点(vertext)或片元(fragment). 任何被着色器使用的OpenGL ES状态值都会自动地被跟踪并且作用于着色器上.这个自动状态跟踪机制允许应用程序为状态管理而使用OpenGL

BGFX 渲染引擎中着色器代码的调试方法

在实时渲染的图形开发中,着色器代码(Shader)越来越复杂,于是单纯的靠经验和不断试错的开发和调试方法早已不能满足实际需求.使用调试工具进行调试,成为开发中重要的方法.Bgfx 是一款跨平台.抽象封装了众多主流图形 API 的优秀渲染引擎.作为示例,本文在 Windows 平台上演示使用 Microsoft Visual Studio* 和 RenderDoc 对 Bgfx 中的 DX11 着色器代码进行调试.了解详情 原文地址:https://www.cnblogs.com/IDZPRC/p