如何在一个shader里面实用2个或2个shader3.0以上的显卡UV坐标值

初入shader的教程第一课 想了解shader来看看吧 | Imagination中文技术社区
我的图书馆
初入shader的教程第一课 想了解shader来看看吧 | Imagination中文技术社区
&前几天发的shader我自己都看不下去了 下面我来好好说一下
Unity shader这个东西呢 也叫shaderlab 编写语言是CgFx 也叫做Direct3D Effects (.FX) 以下是一个shader的基本形式
Shader "MyShader" {
Properties {
_MyTexture ("My Texture", 2D) = "white" { }
// 颜色和矢量等的属性 记述在这个地方
SubShader {
// 以下是本体记述
// - 表面着色器
// - 片段着色器或者顶点
// - 固定着色器
SubShader {
// 这个地方如果你的显卡比较旧 在这里运行 (简化版着色器)
Shader形式
下面是开始的时候 提取文件着色器 做的修改
Shader "Structure Example" {
Properties {
//显示在属性查看器里面的(文理或者颜色)
SubShader {
//着色器的顶点.
SubShader {
// 使用四个文理通道的着色器
SubShader {
// . 使用两个文理通道的着色器
SubShader {
// 要是不怎么好看也会执行的着色器
Fallback "Other Shader Name" // 如果前面都不行 在执行这个
顺便一提 当以斜杠分隔(“/”),就可以按类别划分。 比如:
在这里注意 有些电脑的GPU不支持固定的着色器!!!!
下面让我们来试一下简单的吧 搞起!
Shader "Solid Red" {
SubShader {
Pass { Color (1,0,0,0) }
通过预制板的命令可以指定颜色,固定着色器这个东西我也说不好。
这里为什么是红色呢,看inspector 片段着色器和顶点着色器变成了固定着色器, 因为代码中咱们命名为red了, 所以无论开不开unity灯都是红色的
来看下面的代码效果 注意 这里要把unity的灯光打开!!
Shader "VertexLit White" {
SubShader {
Material {
Diffuse (1,1,1,1)
Ambient (1,1,1,1)
Lighting On
接下来 unity灯不要关 保持ON状态!!Material预制板中 Diffuse和Ambient 来设定。这两个东西 Diffuse是对象的基本颜色(扩散的颜色),Ambient 可以从Render Settings 里面改变, 环境光如果你不指定的话,他不会受影响。
让我们继续 在inspector里让设定颜色变为可能!
Shader "VertexLit Simple" {
Properties {
_Color ("Main Color", COLOR) = (1,1,1,1)
SubShader {
Material {
Diffuse [_Color]
Ambient [_Color]
Lighting On
来吧让我们继续
Shader "VertexLit" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" {}
SubShader {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
Combine texture * primary DOUBLE, texture * primary
看到这里,有了一些新的命令不需要全部记住。可能比较难,慢慢掌握。
可以这样做修改 看下面
Properties {
_RangeTest ("Range Test", Range (0.0, 1.0)) = 0.5
_FloatTest ("Float Test", Float) = 0.5
_ColorTest ("Color Test", Color) = (0.5, 0.5, 0.5, 0.5)
_VectorTest ("Vector Test", Vector) = (0.5, 0.5, 0.5)
_2DTest ("2D Test", 2D) = "white" {}
_RectTest ("Rect Test", Rect) = "white" {}
_CubeTest ("Cube Test", Cube) = "white" {}
固定着色器有点难,但是可以让许多颜色变为可能。需要写复杂的代码,比如渲染路径 和通道 ,我们来试试简单的。
Shader "Example/Diffuse Simple" {
Properties {
_MainColor("Color", Color) = (1,1,1)
SubShader {
Tags { "RenderType" = "Opaque" }
#pragma surface surf Lambert
struct Input {
float4 color : COLOR;
float4 _MainC
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = _MainColor.
Fallback "Diffuse"
很多新东西, 一个一个去看。首先,表面着色,最开始的pragma这个语句,表面着色器。看下面代码。
#pragma surface surfaceFunction lightModel [optionalparams]
「surfaceFunction」这个是一个相当于切入点
「lightModel」是嵌入照明
「optionalparams」这个是通过让alpha顶点变形时,要vertex等指定进行。
现在来看看这个函数的入口。
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = _MainColor.
他作为一种形式。他可以Input输入,在SurfaceOutput里进行处理。
如果你什么也没写,他会变黑。
struct Input {
float4 color : COLOR;
Input可以来弄UV坐标 但不是说设置了变量。
SurfaceOutput可以设置反光。
struct SurfaceOutput {
这里可以看见各种指定变化,比如Properties在被_MainColor的三元色照看着。你设定成红色就是红色,我们改良一下optionalparams 让颜色变得透明一点。
Shader "Example/Diffuse Simple" {
Properties {
_MainColor("Color", Color) = (1,1,1)
SubShader {
Tags { "RenderType" = "Transparent" }
float4 _MainC
#pragma surface surf Lambert alpha
struct Input {
float4 color : COLOR;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = _MainColor.rgb * half3(1, 0.5, 0.5);
o.Alpha = 0.5;
Fallback "Diffuse"
这里在pragma后面加了alpha,这个就是说他们可以混合,咱们刚刚写了0.5么。
下面我们来干点别的吧。
Shader "Example/Diffuse Texture" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
SubShader {
Tags { "RenderType" = "Opaque" }
#pragma surface surf Lambert
struct Input {
float2 uv_MainT
sampler2D _MainT
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).
Fallback "Diffuse"
变成这样的原因呢,是因为UV坐标 在input里面添加了纹理坐标。 使用CG的内置函数的tex2D,因为他们看不到纹理的色彩坐标,所以。。。
然后,我们要输入法线地图和阴影装饰等内在函数。如果用什么样的代码写的话,来吧我们干点什么。
让我们变一下顶点
Shader "Example/Transform Vertex" {
SubShader {
Tags { "RenderType" = "Opaque" }
#pragma surface surf Lambert vertex:vert
struct Input {
float4 color : COLOR;
void vert (inout appdata_full v) {
v.vertex.x += 0.2 * v.normal.x * sin(v.vertex.y * 3.14 * 16);
v.vertex.z += 0.2 * v.normal.z * sin(v.vertex.y * 3.14 * 16);
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = half3(1, 0.5, 0.5);
Fallback "Diffuse"
哈哈 有趣么
在Properties变量拉的话就能变形啦。但是,上述的例子。只是单纯的顶点位置移动而已,所以有奇怪的感觉。
作为固定着色器。我一直用SM2.0 意思是支持Shader Model2.0。” 2.0对应的GPU比较多。
这些比较。。额 灵活吧看下面
Shader "Custom/SolidColor" {
SubShader {
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
return mul (UNITY_MATRIX_MVP, v);
fixed4 frag() : COLOR {
return fixed4(1.0,0.0,0.0,1.0);
在这次记述中,pragma的顶点 片段着色器 和顶点着色器 比较简单 vert顶点着色器,对所有的顶点坐标进行变化。在这里,参数中被给予的顶点坐标UNITY _ MATRIX _ MVP的嵌入式值的MVP矩阵 会在显示器在哪个位置进行计算的二维显示
FRAG是片段着色器,我们要会计算所有像素。看到刚刚的这里的顶点不透明的红色,是因为你还没有做光照计算,它已经成为完全的红色。
恩。。。。 VERT已被指定为接收类型为float4 但还是应该得到什么?它被指定为一种形式,位置。
额!我也理解的不好 看下表
该模型的局部坐标
第二个UV坐标
Description
SV_POSITION
MVP&变换后的坐标
MVP&变化后的法线
第一UV纹理坐标
第二UV纹理坐标
float4, fixed4
线性插值的颜色
float4, fixed4
线性插值的颜色(这个我也不知道)
标签。没有什么值
Shader "Custom/WindowCoordinates/Base" {
SubShader {
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 vert(appdata_base v) : POSITION {
return mul (UNITY_MATRIX_MVP, v.vertex);
fixed4 frag(float4 sp:WPOS) : COLOR {
fixed2 red_green = sp.xy / _ScreenParams.
fixed blue = 0.0;
fixed alpha = 1.0f;
return fixed4(red_green, blue, alpha);
appdata _ base的构造体,其中vertex,normal,texcoord 这个3个东西,各自POSITION NORMAL,TEXCOORD 0的定义给予宣言。然后 ,你将使用的是作为一个参数传递给VERT这种情况。它也是现在使用的方式名为UnityCG.cginc 这个文件已被宣布为appdata_base,并将其列入。
Shader "Custom/WindowCoordinates/Base" {
SubShader {
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
float4 vert(appdata_base v) : POSITION {
return mul (UNITY_MATRIX_MVP, v.vertex);
fixed4 frag(float4 sp:WPOS) : COLOR {
fixed2 red_green = sp.xy / _ScreenParams.
fixed blue = 0.0;
fixed alpha = 1.0f;
return fixed4(red_green, blue, alpha);
除了结构的各种辅助功能,appdata_base也是UnityCG.cginc准备的。
好了让我们再看看着色器的代码。
在屏幕中的片段着色器的坐标 是通过_ScreenParams.xy划分的值,屏幕左下角(0,0),右上(1,1)。
到这里 请给位检查一下 是否你们是着色器3.0的 咱们要配合用WPOS。。
return fixed4(fixed2(1.0), 0.0, 1.0);
用fixed2(1.0),改变(1.0,1.0),fixed4是一种形式展开,(1.0,1.0,0.0,1.0),这是黄色。
恩 ,我们要看到更广泛的UV坐标 使用在UnityCG.cginc定义顶点着色器的vert_img很方便,在v2f_img里去弄。
Shader "Custom/TextureCoordinates/UV" {
SubShader {
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
float4 frag(v2f_img i) : COLOR {
return float4(i.uv, 0.0, 1.0);
现在一个面的颜色完整了。 现在我们让他变完整 使用Properties
Shader "Custom/TextureCoordinates/Hecomi" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
SubShader {
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainT
float4 frag(v2f_img i) : COLOR {
return tex2D(_MainTex, i.uv);
下面我给出几个列子 大家自己看看
Shader "Custom/Chou" {
Properties {
_BendScale("Bend Scale", Range(0.0, 1.0)) = 0.2
_MainTex("Main Texture", 2D) = "white" {}
SubShader {
Tags { "RenderType" = "Transparent" }
Cull Off Zwrite On
#pragma surface surf Lambert alpha vertex:vert
#define PI 3.14159
struct Input {
float2 uv_MainT
float4 color : C
sampler2D _MainT
float _BendS
void vert (inout appdata_full v) {
float bend = sin(PI * _Time.x * 1000 / 45 + v.vertex.y);
float x = sin(v.texcoord.x * PI) - 1.0;
float y = sin(v.texcoord.y * PI) - 1.0;
v.vertex.y += _BendScale * bend * (x + y);
void surf (Input IN, inout SurfaceOutput o) {
float4 tex = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = IN.color.
o.Alpha = IN.color.a * tex.a;
Fallback "Diffuse"
Shader "Custom/Chou" {
Properties {
_BendScale("Bend Scale", Range(0.0, 1.0)) = 0.1
_MainTex ("Base (RGB)", 2D) = "white" {}
SubShader {
Blend SrcAlpha One
#pragma vertex vert
#pragma fragment frag
#define PI 3.14159
#include "UnityCG.cginc"
uniform float _BendS
uniform sampler2D _MainT
struct v2f {
float4 position : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
v2f vert(appdata_full v) {
float bend = sin(PI * _Time.x * 1000 / 45 + v.vertex.y + v.vertex.x);
float x = sin(v.texcoord.x * PI) - 1.0;
float y = sin(v.texcoord.y * PI) - 1.0;
v.vertex.y += _BendScale * bend * (x + y);
o.position = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord);
o.color = v.
fixed4 frag(v2f i) : COLOR {
fixed4 tex = tex2D(_MainTex, i.uv);
tex.rgb *= i.color.
tex.a *= i.color.a;
好了就告一段落吧。
TA的推荐TA的最新馆藏[转]&
喜欢该文的人也喜欢温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
一个专注于天空魅力的人
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
阅读(547)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_095065',
blogTitle:'使用基于GPU的Geometry Clipmap渲染地形(Terrain Rendering Using GPU-Based Geometry Clipmaps)(下)',
blogAbstract:'翻译:clayman
clayman_.cn
仅供个人学习使用,勿用于任何商业用途,转载请注明作者^_^
1.3.5 Vertex Shader
我们使用同一个vertex shader来渲染之前描述的所有2D footprint。首先,对于给定的footprint坐标(x,y)来说,shader通过简单的缩放和变换来计算它的世界坐标(x,y)。接下来,从顶点纹理中读取高度值z。这里不需要任何过滤器,因为顶点和纹理采样是一一对应的。 ',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:2,
publishTime:0,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'一个专注于天空魅力的人',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}君,已阅读到文档的结尾了呢~~
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
数字电影生产线上数字交换格式研究毕业论文
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口Unity3D Shader 入门
发布时间: 10:26:31
什么是Shader
Shader(着色器)是一段能够针对3D对象进行操作、并被GPU所执行的程序。Shader并不是一个统一的标准,不同的图形接口的Shader并不相同。OpenGL的着色语言是GLSL, NVidia开发了Cg,而微软的Direct3D使用高级着色器语言(HLSL)。而Unity的Shader 是将传统的图形接口的Shader(由 Cg / HLSL编写)嵌入到独有的描述性结构中而形成的一种代码生成框架,最终会自动生成各硬件平台自己的Shader,从而实现跨平台。
Unity Shader 其实并不难,初学者往往很迷惑是因为它有太多固定的命令和结构,而这些命令又需要我们对3D渲染有一定的了解才能知道它们是做什么的。
Shader种类
OpenGL和Direct3D都提供了三类着色器:
顶点着色器:处理每个顶点,将顶点的空间位置投影在屏幕上,即计算顶点的二维坐标。同时,它也负责顶点的深度缓冲(Z-Buffer)的计算。顶点着色器可以掌控顶点的位置、颜色和纹理坐标等属性,但无法生成新的顶点。顶点着色器的输出传递到流水线的下一步。如果有之后定义了几何着色器,则几何着色器会处理顶点着色器的输出数据,否则,光栅化器继续流水线任务。 像素着色器(Direct3D),常常又称为片断着色器(OpenGL):处理来自光栅化器的数据。光栅化器已经将多边形填满并通过流水线传送至像素着色器,后者逐像素计算颜色。像素着色器常用来处理场景光照和与之相关的效果,如凸凹纹理映射和调色。名称片断着色器似乎更为准确,因为对于着色器的调用和屏幕上像素的显示并非一一对应。举个例子,对于一个像素,片断着色器可能会被调用若干次来决定它最终的颜色,那些被遮挡的物体也会被计算,直到最后的深度缓冲才将各物体前后排序。 几何着色器:可以从多边形网格中增删顶点。它能够执行对CPU来说过于繁重的生成几何结构和增加模型细节的工作。Direct3D版本10增加了支持几何着色器的API, 成为Shader Model 4.0的组成部分。OpenGL只可通过它的一个插件来使用几何着色器。
Unity Shader 分为 表面着色器(Surface Shader)和 顶点片段着色器(Vertex And Fragment Shader)。
表面着色器(Surface Shader)是Unity提出的一个概念。编写着色器与光照的交互是复杂的,光源有很多类型,不同的阴影选项,不同的渲染路径(正向和延时渲染),表面着色器将这一部分简化。Unity建议使用表面着色器来编写和光照有关的Shader。 顶点片段着色器(Vertex And Fragment Shader)和OpenGL,Direct3D中的顶点着色器和片段着色器没有什么区别。顶点片段着色器比表面着色器使用更自由也更强大,当然光照需要自行处理。Unity也允许在里面编写几何着色器,一般用得不多。
Shader程序结构
Shader语法:
//Shader语法: Shader "name" { [Properties] Subshaders [Fallback] [CustomEditor] }
//Properties 语法 Properties { Property [Property ...] }
// Subshader 语法 Subshader { [Tags] [CommonState] Passdef [Passdef ...] }
// Pass 语法 Pass { [Name and Tags] [RenderSetup] }
// Fallback 语法 Fallback "name"
属性定义(Property Definition):定义Shader的输入,这些输入可以在材质编辑的时候指定 子着色器(SubShader):一个Shader可以有多个子着色器。这些子着色器互不相干且只有一个会在最终的平台运行。编写多个的目的是解决兼容性问题。Unity会自己选择兼容终端平台的Shader运行。 回滚(Fallback):如果子着色器在终端平台上都无法运行,那么使用Fallback指定的备用Shader,俗称备胎。 Pass:一个Pass就是一次绘制。对于表面着色器,只能有一个Pass,所以不存在Pass节。顶点片段着色器可以有多个Pass。多次Pass可以实现很多特殊效果,例如当人物被环境遮挡时还可以看到人物轮廓就可以用多Pass来实现。 Cg代码:每个Pass中都可以包含自定义的Cg代码,从CGPROGRAM开始到ENDCG结束。
基本的表面着色器示例:
Shader "Custom/NewShader" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
SubShader {
Tags { "RenderType" = "Opaque" }
#pragma surface surf Lambert
sampler2D _MainT
struct Input {
float2 uv_MainT
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.
o.Alpha = c.a;
FallBack "Diffuse"
基本的顶点片段着色器示例:
Shader "VertexInputSimple" {
SubShader {
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
v2f vert (appdata_base v)
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.color.xyz = v.normal * 0.5 + 0.5;
o.color.w = 1.0;
fixed4 frag (v2f i) : SV_Target { return i. }
Shader 输入
Shader的输入有两个来源,一是通过属性定义,一是通过Shader.SetGlobalXXX方法全局设置。
属性定义变量:属性定义中的变量是Shader参数的主要设置方式。 它是随材质变化的,每个使用该Shader的材质都可以在Inspector或者脚本中设置这些参数。这些参数除了在Shader的Properties段中定义外,还需要在Cg中声明方可使用。例如上面表面着色器的例子中我们定义了_MainTex这个类型为2D的属性,还需要在Cg中声明 sampler2D _MainTex。 全局变量:Shader有一组SetGlobalXXX方法,可以对Shader的在Cg中定义而没有在属性中定义的uniform变量进行设置。这个设置是全局的,所有定义了该uniform的Shader都会受到影响。例如我们希望场景随着时间变化而改变颜色,就可以给场景所使用到的Shader设置统一的全局颜色变量,然后在脚本中通过设置该颜色来改变场景的颜色。在角色释放技能时场景变黑也可以使用这个方法。
Unity shader 中允许定义的属性类型有:
对应Cg类型
_MyFloat (“My float”, Float) = 0.5
浮点数 (在指定范围内)
_MyRange (“My Range”, Range(0.01, 0.5)) = 0.1
浮点四元组
_MyColor (“Some Color”, Color) = (1,1,1,1)
浮点四元组
_MyVector(“Some Vector”,Vector)
= (1,1,1,1)
2的阶数大小的贴图
_MyTexture (“Texture”, 2D) = “white” {}
非2的阶数大小的贴图
_MyRect(“My Rect”, Rect) = “white” {}
samplerCUBE
_MyCubemap (“Cubemap”, CUBE) = “” {}
注:CubeMap 是6张有联系的2D贴图的组合主要用来做反射效果(比如天空盒和动态反射)
SubShader中除了Pass,有两个标签值得关注:LOD和Tags
LOD是 Level of Detail的简写,确切地说是Shader Level of Detail的简写,因为Unity中还有一个模型的LOD概念,这是两个不同的东西。我们这里只介绍Shader中LOD,模型的LOD请。
Shader LOD 就是让我们设置一个数值,这个数值决定了我们能用什么样的Shader。可以通过Shader.maximumLOD或者Shader.globalMaximumLOD 设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。通过LOD,我们就可以为某个材质写一组SubShader,指定不同的LOD,LOD越大则渲染效果越好,当然对硬件的要求也可能越高,然后根据不同的终端硬件配置来设置 globalMaximumLOD来达到兼顾性能的最佳显示效果。
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
SubShader可以被若干的标签(tags)所修饰,而硬件将通过判定这些标签来决定什么时候调用该着色器。
比较常见的标签有:
这个标签很重要,它定义了一个整数,决定了Shader的渲染的次序,数字越小就越早被渲染,早渲染就意味着可能被后面渲染的东西覆盖掉看不见。
预定义的Queue有:
Background
最早被调用的渲染,用来渲染天空盒或者背景
这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的)
用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
Transparent
以从后往前的顺序渲染透明物体
用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)
RenderType
“Opaque”或”Transparent”是两个常用的RenderType。如果输出中都是非透明物体,那写在Opaque里;如果想渲染透明或者半透明的像素,那应该写在Transparent中。这个Tag主要用,一般情况下这Tag好像也没什么作用。
CommonState
SubShader中可以定义一组Render State,基本上就是一些渲染的开关选项,他们对该SubShader的所有的Pass都有效,所以称Common。这些Render State也可以在每个Pass中分别定义,将在Pass中详细介绍。
Render State
Render State主要就是控制渲染过程的一些开关选项,例如是否开启alpha blending ,是否开启depth testing。
常用的Render State有:
用法:Cull Back | Front | Off
多边形表面剔除开关。Back表示背面剔除,Front表示正面剔除,Off表示关闭表面剔除即双面渲染。有时候如裙摆,飘带之类很薄的东西在建模时会做成一个面片,这就需要设置Cull Off来双面渲染,否则背面会是黑色。 ZWrite
用法:ZWrite On | Off
控制当前对象的像素是否写入深度缓冲区(depth buffer),默认是开启的。一般来说绘制不透明物体的话ZWrite开启,绘制透明或半透明物体则ZWrite关闭。
深度缓冲区:当图形处理卡渲染物体的时候,每一个所生成的像素的深度(即 z 坐标)就保存在一个缓冲区中。这个缓冲区叫作 z 缓冲区或者深度缓冲区,这个缓冲区通常组织成一个保存每个屏幕像素深度的 x-y 二维数组。如果场景中的另外一个物体也在同一个像素生成渲染结果,那么图形处理卡就会比较二者的深度,并且保留距离观察者较近的物体。然后这个所保留的物体点深度保存到深度缓冲区中。最后,图形卡就可以根据深度缓冲区正确地生成通常的深度感知效果:较近的物体遮挡较远的物体。
理解了深度缓冲区也就理解了为什么绘制透明或半透明物体需要关闭ZWrite, 如果不关闭,透明物体的depth也会被写入深度缓冲区,从而会剔除掉它后面的物体,后面的物体就不会被渲染,看不见后面的物体还能叫透明吗?因此我们使用Alpha blending的时候需要设置ZWrite Off。 ZTest
用法:ZTest (Less | Greater | LEqual | GEqual | Equal | NotEqual | Always)
控制如何进行深度测试,也就是上面说的图形处理卡比较二者的深度的比较方法。默认是LEqual。
值得一提的是使用Aplha blending的时候ZWrite需要关闭但是ZTest是要开启的,因为如果透明物体前面还有不透明物体,透明物体还是应该被遮挡剔除的。 Blend
混合。控制了每个Shader的输出如何和屏幕上已有的颜色混合。
Blend Off: 关闭混合
Blend SrcFactor DstFactor:最终颜色 = Shader产生的颜色 × SrcFactor + 屏幕上原来的颜色 × DstFactor
Blend SrcFactor DstFactor, SrcFactorA DstFactor:和上面一样,只是Alpha通道使用后面两个参数计算
常用的Blend模式有:
Blend SrcAlpha OneMinusSrcAlpha // Alpha blending
Blend One One // Additive
Blend OneMinusDstColor One // Soft Additive
Blend DstColor Zero // Multiplicative
Blend DstColor SrcColor // 2x Multiplicative
Unity5开始下列固定功能的Shader命令被标记为过时了,这些命令的功能现在建议在Shader(Cg)中通过代码来实现,这里列出是为了方便阅读以前写的Shader:
Lighting On | Off Material { Material Block } SeparateSpecular On | Off Color Color-value ColorMaterial AmbientAndDiffuse | Emission Fog { Fog Block } AlphaTest (Less | Greater | LEqual | GEqual | Equal | NotEqual | Always) CutoffValue SetTexture textureProperty { combine options }
Surface Shader
Surface Shader 隐藏了很多光照处理的细节,它的设计初衷是为了让用户仅仅使用一些指令(#pragma)就可以完成很多事情,并且封装了很多常用的光照模型和函数。相比底层的Vertex And Fragment Shader,Suface Shader的限制比较多,它只能有一次Pass。如果做一些常规的功能又需要光照,可以用Surface Shader写,比较快速便捷。如果要写比较高级的Shader还是建议使用Vertex Shader 和 Fragment Shader。
Surface Shader主要有两部分组成,一个是#pragma后面的指令,一个是surf函数。
pragma的语法是 #pragma surface surfaceFunction lightModel [optionalparams]
- surfaceFunction 通常就是名为surf的函数, 函数名可以自己取
surf函数原型是:void surf (Input IN, inout SurfaceOutput o)
- lightModel是Unity内置的光照模型,可以是Lambert,Blinn-Phong等。
- optionalparams: 包含很多指令
surf函数主要有一个Input结构的输入和SurfaceOutput结构的输出。
Input 结构需要在Shader中定义。它可以包含如下字段, 如果你定义了这些字段就可以在surf函数中使用它们(好神奇的黑科技)
多个贴图的uv坐标,名字必须符合格式uv+贴图名。例如 float2 uv_MainTex float3 viewDir - 视图方向( view direction)值。为了计算视差效果(Parallax effects),边缘光照(rim lighting)等,需要包含视图方向( view direction)值。 float4 with COLOR semantic - 每个顶点(per-vertex)颜色的插值。 float4 screenPos - 屏幕空间中的位置。 为了反射效果,需要包含屏幕空间中的位置信息。比如在Dark Unity中所使用的 WetStreet着色器。 float3 worldPos - 世界空间中的位置。 float3 worldRefl - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。 请参考这个例子:Reflect-Diffuse 着色器。 float3 worldNormal - 世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。 float3 worldR INTERNAL_DATA - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。 float3 worldN INTERNAL_DATA -世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。
SurfaceOutput
SurfaceOutput 描述了表面的特性(光照的颜色反射率、法线、散射、镜面等),这个结构是固定的,不需要在Shader中再定义。
struct SurfaceOutput {
//反射率,一般就是在光照之前的原始颜色
//自发光,用于增强物体自身的亮度,使之看起来好像可以自己发光
Unity5 由于引入了基于物理的光照模型,所以新增加了两个Output
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 };
Unity提供了一些基本的SurfaceShader的例子,有助于我们理解输入输出是如何被使用的。
Vertex Shader
如果不想使用Surface Shader而直接编写opengl和Direct3D中常见的顶点着色器和片段着色器,可以通过Cg代码段嵌入到Pass中:
// ... the usual pass state setup ...
// compilation directives for this snippet, e.g.:
#pragma vertex vert
#pragma fragment frag
// the Cg/HLSL code itself
// ... the rest of pass setup ...
其中vert就是顶点着色器函数,frag就是片段着色器函数。一般来说,可以在顶点着色器中进行的计算就不应该放到片段着色器中去算,因为顶点着色器是逐顶点计算的而片段着色器是逐像素计算的,一个模型顶点总比表明像素少很多吧。
编写顶点和片段着色器一般需要包含Unity预定义的一个帮助文件UnityCG.cginc,里面预定义了一些常用的结构和方法。版Unity这个文件位于({unity install path}/Data/CGIncludes/UnityCG.cginc。 Mac版位于/Applications/Unity/Unity.app/Contents/CGIncludes/UnityCG.cginc。
在代码中我们只需要添加 #include "UnityCG.cginc"就可以使用里面的结构和方法。
顶点着色器的原型是 v2f vert (appdata v)
appdata 是输入,可以自己定义也可以使用Unity预定义的。Unity在UnityCG.cginc预定义了三种常用的输入结构:appdata_base,appdata_tan,appdata_full。
struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0; };
struct appdata_tan {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0; };
struct appdata_full {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3; #if defined(SHADER_API_XBOX360)
half4 texcoord4 : TEXCOORD4;
half4 texcoord5 : TEXCOORD5; #endif
fixed4 color : COLOR; };
我们注意到这些结构的字段和表面着色器中的字段不同,后面多了一个冒号和一个标签。这是该字段的语义,用于告诉GPU这个字段的数据应该去哪里读写。GPU毕竟是为了图形计算而特别设计的东西,很多东西都是固定的,我们只要记得有这么几个名字可以用行了。
顶点在模型坐标系下的位置
顶点的法向量
顶点的切向量
顶点的第一个uv坐标
顶点的第二个uv坐标,最多可以到5
顶点着色器的输出是也是一个可以自己定义的结构,但是结构内容也是比较固定的,一般包含了顶点投影后的位置,uv,顶点色等,也可以加一些后面片段着色器需要用到但是需要在顶点着色器中计算的值。这个输出就是后面片段着色器的输入。
struct v2f
float4 pos : SV_POSITION;
: TEXCOORD0;
可以使用的字段有:
SV_POSITION
顶点在投影空间下的位置,注意和输入的模型坐标系下的位置不同,这个字段必必须设置,这个坐标转换是顶点着色器的重要工作
顶点在视图坐标系下的法向量
第一张贴图的uv坐标
第二张贴图的uv坐标
切向量,主要用来修正法线贴图Normal Maps
第一个定点色
第二个定点色
其他自定义的字段
顶点着色器有一项重要的工作就是进行坐标变换。顶点着色器的输入中的坐标是模型坐标系(ObjectSpace)下的坐标,而最终绘制到屏幕上的是投影坐标。
在我们Shader里面只需要一句话就可以完成坐标的转换,这也是最简单的顶点着色器:
v2f vert(appdata v) {
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
用UNITY_MATRIX_MVP矩阵乘以顶点在模型坐标系下的坐标就得到投影坐标。
UNITY_MATRIX_MVP是Unity内建的模型-&视-&投影矩阵, Unity内建矩阵如下:
UNITY_MATRIX_MVP:当前模型-&视图-&投影矩阵。(注:模型矩阵为 本地-&世界) UNITY_MATRIX_MV:当前模型-&视图矩阵 UNITY_MATRIX_V:当前视图矩阵 UNITY_MATRIX_P:当前投影矩阵 UNITY_MATRIX_VP:当前视图-&投影矩阵 UNITY_MATRIX_T_MV:转置模型-&视图矩阵 UNITY_MATRIX_IT_MV:逆转置模型-&视矩阵, 用于将法线从ObjectSpace旋转到WorldSpace。为什么法线变化不能和位置变换一样用UNITY_MATRIX_MV呢?一是因为法线是3维的向量而- UNITY_MATRIX_MV是一个4x4矩阵,二是因为法线是向量,我们只希望对它旋转,但是在进行空间变换的时候,如果发生非等比缩放,方向会发生偏移。 UNITY_MATRIX_TEXTURE0 to UNITY_MATRIX_TEXTURE3:纹理变换矩阵
下面简单介绍一下里面提到的几个坐标系:
模型坐标系:也叫物体坐标系,3D建模的时候每个模型都是在自己的坐标系下建立的,如果一个人物模型脚底是(0,0,0) 点的话它的身上其它点的坐标都是相对脚底这个原点的。
世界坐标系:我们场景是一个世界,有自己的原点,模型放置到场景中后模型上的每个顶点就有了一个新的世界坐标。这个坐标可以通过模型矩阵×模型上顶点的模型坐标得到。
视图坐标系:又叫观察坐标系,是以观察者(相机)为原点的坐标系。场景中的物体只有被相机观察到才会绘制到屏幕上,相机可以设置视口大小和裁剪平面来控制可视范围,这些都是相对相机来说的,所以需要把世界坐标转换到视图坐标系来方便处理。
投影坐标系:场景是3D的,但是最终绘制到屏幕上是2D,投影坐标系完成这个降维的工作,投影变换后3D的坐标就变成2D的坐标了。投影有平行投影和透视投影两种,可以在Unity的相机上设置。
屏幕坐标系 : 最终绘制到屏幕上的坐标。屏幕的左下角为原点。
除了内建矩阵,Unity还内建了一些辅助函数也可以在顶点着色器里面使用:
float3 WorldSpaceViewDir (float4 v):根据给定的局部空间顶点位置到相机返回世界空间的方向(非规范化的) float3 ObjSpaceViewDir (float4 v):根据给定的局部空间顶点位置到相机返回局部空间的方向(非规范化的) float2 ParallaxOffset (half h, half height, half3 viewDir):为视差法线贴图计算UV偏移 fixed Luminance (fixed3 c):将颜色转换为亮度(灰度) fixed3 DecodeLightmap (fixed4 color):从Unity光照贴图解码颜色(基于平台为RGBM 或dLDR) float4 EncodeFloatRGBA (float v):为储存低精度的渲染目标,编码[0..1)范围的浮点数到RGBA颜色。 float DecodeFloatRGBA (float4 enc):解码RGBA颜色到float。 float2 EncodeViewNormalStereo (float3 n):编码视图空间法线到在0到1范围的两个数。 float3 DecodeViewNormalStereo (float4 enc4):从enc4.xy解码视图空间法线
Fragment Shader
原文地址:
版权声明:本文为博主原创文章,未经博主允许不得转载。
来源:http://blog.csdn.net/ring0hx/article/details/}

我要回帖

更多关于 如何编写shadermod 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信