Shader开发之三大着色器
固定功能管线着色器Fixed Function Shaders
固定功能管线着色器的关键代码一般都在Pass的材质设置Material{}和纹理设置SetTexture{}部分。
Shader "Custom/VertexList" {
Properties {
_Color("Main Color",Color) = (,,,0.5)
_SpecColor("Spec Color",Color) = (,,,)
_Emission("Emissive Color",Color) = (,,,)
_Shininess("Shininess",Range(0.01,)) = 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光照模型,并设置表面颜色为白色。
Shader "Custom/Diffuse Simple" {
SubShader{
Tags{"RenderType" = "Opaque"}
CGPROGRAM//表面着色器的实现代码
//指明着色器类型,表面函数和光照模型
#pragma surface surf Lambert
struct Input{//输入的数据结构体
float color : COLOR;
};
void surf(Input IN,inoutSurfaceOutput o){//表面函数
o.Albedo = ;//输出颜色值
}
ENDCG;
}
Fallback "Diffuse"//备选着色器
}
渲染效果如下:

2、在示例1的基础上添加纹理,代码如下:
Shader "Custom/Diffuse Simple" {
Properties{//添加纹理属性
_MainTex("Texture",2D) = "White"{}
}
SubShader{
Tags{"RenderType" = "Opaque"}
CGPROGRAM //表面着色器的实现代码
//指明着色器类型,表面函数和光照模型
#pragma surface surf Lambert
struct Input{//输入的数据结构体
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf(Input IN,inout SurfaceOutput o){//表面函数
o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"//备选着色器
}
渲染效果如下:

3、在示例2的基础上添加法线贴图
Shader "Custom/Diffuse Simple"{
Properties{//添加纹理属性
_MainTex("Texture",2D) = "white"{}
//添加法线贴图属性
_BumpMap("Bumpmap",2d) = "bump"
}
SubShader{
Tags{"RenderType" = "Opaque"}
CGPROGRAM//表面着色器的实现代码
//指明着色器类型,表面函数和光照模型
#pragma surface surf Lambert
struct Input{//输入的数据结构体
float2 uv_MainTex;
float2 uv_BumpMap;
};
sampler2D _MainTex;
sampler2D _BumpMap;
void surf(Input IN,inout SurfaceOutput o){//表面函数
o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));
}
ENDCG
}
Fallback "Diffuse"//备选着色器
}
渲染效果如下:

4、添加立方体贴图反射
Shader "Surface Shader Example/Diffuse Simple"{
Properties{//添加纹理属性
_MainTex("Texture",2D) = "white"{}
//立方体贴图属性
_Cube("Cubemap",CUBE) = ""{}
}
SubShader{
Tags{"RenderType" = "Opaque"}
CGPROGRAM//表面着色器的实现代码
//指明着色器类型,表面函数和光照模型
#pragma surface surf Lambert
struct Input{//输入的数据结构体
float2 uv_MainTex;
float3 worldRefl;//输入反射参数
};
sampler2D _MainTex;
SamplerCUBE _Cube;
void surf(Input IN,inout SurfaceOutput o){//表面函数
o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
//将发射颜色设置给自发光颜色
o.Emission = texCUBE(_Cube,IN.worldRefl).rgb;
}
ENDCG
}
Fallback "Diffuse"//备选着色器
}
渲染效果如下:

顶点片段着色器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)根据发现方向设置模型表面颜色
Shader"Tutorial/Display Normals"{
SubShader{
Pass{
CGPROGRAM
//CG代码块开始
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f{
float4 pos:SV_POSITION;
float3 color:COLOR0;
};
v2f vert(appdata_base v)
{//顶点程序代码,计算位置和颜色
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.color = v.normal*0.5+0.5;
return o;
}
half4 frag(v2f i):COLOR
{
//片段程序代码,直接把输入的颜色返回,并把透明度设置为1
return half(i.color,);
}
ENDCG
}
}
Fallback "VertexLit"
}
渲染效果如下:

2)根据切线方向设置模型表面颜色。
Shader"vertex and fragment example/Tangents"{
SubShader{
Pass{
Fog{Mode Off}
CGPROGRAM
//CG代码块开始
#pragma vertex vert
#pragma fragment frag
//输入位置和切线数据
struct appdata{
float4 vertex : POSITION;
float4 tangent : TANGENT;
};
struct v2f{
float4 pos : POSITION;
fixed4 color : COLOR;
};
v2f vert(appdata_base v)
{//顶点程序代码,计算位置和颜色
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.color = v.tangent*0.5+0.5;
return o;
}
fixed4 frag(v2f i):COLOR0
{
//片段程序代码,直接把输入的颜色返回,并把透明度设置为1
return i.color;
}
ENDCG
}
}
}
渲染效果如下:

Shader开发之三大着色器的更多相关文章
- 片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但
片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但片元着色器是一个更合适的名字, 因为此时的片元并不是一个真正意义上的像素.
- OpenGL 4.0的Tessellation Shader(细分曲面着色器)
细分曲面着色器(Tessellation Shader)处于顶点着色器阶段的下一个阶段,我们可以看以下链接的OpenGL渲染流水线的图:https://www.opengl.org/wiki/Rend ...
- (译)Minimal Shader(最小的着色器)
(原文:https://en.wikibooks.org/wiki/Cg_Programming/Unity/Minimal_Shader) This tutorial covers the basi ...
- Unity3d之Shader编程:子着色器、通道与标签的写法 & 纹理混合
一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器 ...
- Initialize the shader 初始化着色器
目录 Loads the shader files and makes it usable to DirectX and the GPU 加载着色器文件并使其可用于DirectX和GPU Compil ...
- Unity Shader着色器优化
https://mp.weixin.qq.com/s?__biz=MzU5MjQ1NTEwOA==&mid=2247493518&idx=1&sn=c51b92e9300bcf ...
- 【Android 应用开发】OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解
最近开始关注OpenGL ES 2.0 这是真正意义上的理解的第一个3D程序 , 从零开始学习 . 案例下载地址 : http://download.csdn.net/detail/han120201 ...
- WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码
原文:WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码 HLSL,High Level Shader Language,高级着色器语言,是 Di ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader)
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader) 代码工 ...
随机推荐
- object-c 获得目录(包括子目录)下所有文件和文件夹路径
void getAllPathNameInDirectory(vector<string>&filePathList,vector<string>&direct ...
- 在linq to entities中无法使用自定义方法
来源: http://support.microsoft.com/kb/2588635/zh-tw (繁体)
- ssh:Permissions 0644 for ‘/root/.ssh/id_rsa’ are too open
最近,用ssh连接github时,突然提示“Permissions 0644 for ‘/root/.ssh/id_rsa’ are too open”,并且断开连接. 仔细阅读了一下ssh文档和这句 ...
- 【Android】第21章 2D图形和动画
分类:C#.Android.VS2015: 创建日期:2016-03-19 一.简介 Android系统定义了一系列独立的图形处理类,其中,2D图形处理类分别位于以下命名空间: Android.Gra ...
- 【Android】19.3 ContentProvider及安卓进一步封装后的相关类
分类:C#.Android.VS2015: 创建日期:2016-03-08 一.简介 ContentProvider:内容提供程序. Android的ContentProvider与.NET框架的EF ...
- 【Android】7.5 RelativeLayout(相对布局)
分类:C#.Android.VS2015: 创建日期:2016-02-11 一.简介 RelativeLayout是一种相对布局,容器中子元素的位置是相对于其前一个元素或者其他元素的位置来计算的,或者 ...
- linux io architecture
http://www.cs.columbia.edu/~krj/os/lectures/L24-IO.pdf http://events.linuxfoundation.org/sites/event ...
- 服务器搭建1 安装mysql数据库
一,安装mysql-service (1)检查系统中是否已经安装mysql 在终端里面输入 sudo netstat -tap | grep mysql 若没有反映,没有显示已安装结果,则没有安装.若 ...
- 输入法环境变量XMODIFIERS/GTK_IM_MODULE
我们配置输入法时,都是习惯性的在输入法启动前导出环境变量: export XMODIFIERS=@im=SCIM export GTK_IM_MODULE=SCIM 他们有何用意呢?? 我们常用的输入 ...
- eclipse 项目中的java文件没有在WEB-INF目录下的classes中 生成相对应的编译后的类
1.首先确定project->Build Automatically是否勾选上: 2.执行完第一步之后测试一下看是否能编译,如果还是不能,则进行手动编译: 3,进入clean对话框,选择Cle ...