unity怎么把unity shader time变小

unity&shader的几个空间的转换问题
首先在着色器里的空间有:模型空间、世界坐标空间、视空间、视锥体、剪切空间。
接下来说介绍一下各个空间是什么以及之间的关系:
模型空间就是平时说的local坐标系,在模型制作时建模师依据的坐标空间。
世界坐标空间:就是所有物体的统一坐标的空间坐标系。
视空间:以摄像机为中心的坐标空间。
视锥体:这个就是视空间的一部分,具体怎么来的是根据摄像机的转向、近平面、远平面所组成的一个视锥体空间。
剪切空间:这个就是投影空间吧,我们将视锥体空间的物体投影到一个二维平面上的空间。当然还有一个函数是用来做投影空间到屏幕坐标的转换的,之前已经提到过了。具体屏幕空间和投影空间的区别:
Direct3D中投影空间内的点坐标与屏幕上(或视口内)点的对应关系,
设屏幕大小为w&h,屏幕左上角像素的中心被定义为(0,0),整个屏幕是从(-0.5,-0.5)-(w-0.5,h-0.5),
将投影空间内的x轴上区间(-1.0-1/w, 1.0-1/w]均匀分成w份,每份长度2/w,
将投影空间内的y轴上区间(-1.0-1/h, 1.0-1/h]均匀分成h份,每份长度2/h。
例如最左侧的像素点的对应的横坐标区间为(-1.0-1/w,-1.0+1/w],依次类推。
对于更一般的情况有
光栅化公式 投影空间坐标(x,y) -& 屏幕像素坐标(Sx,Sy)
Sx = x * (w/2) + (w/2)&&
Sy = y * (-h/2) + (h/2)& [2]
详情参考:/luckydmz/archive//69779.html
各个坐标空间的转换内置矩阵如下:
UNITY_MATRIX_MVP&&&&&&&
当前模型视图投影矩阵
UNITY_MATRIX_MV&&&&&&&&&&
当前模型视图矩阵
UNITY_MATRIX_V&&&&&&&&&&&&&
当前视图矩阵。
UNITY_MATRIX_P&&&&&&&&&&&&&
目前的投影矩阵
UNITY_MATRIX_VP&&&&&&&&&&
&当前视图*投影矩阵
UNITY_MATRIX_T_MV&&&&&&
移调模型视图矩阵
UNITY_MATRIX_IT_MV&&&&&
模型视图矩阵的逆转
UNITY_MATRIX_TEXTURE0&&
UNITY_MATRIX_TEXTURE3&&&&&&&&&
纹理变换矩阵
Vectors (float4) supported:
&向量(仅float4)支持:UNITY_LIGHTMODEL_AMBIENT&&&&&&&&当前环境的颜色。
所有的矩阵都是可以transform组件来获得,通过乘以对应矩阵的逆就可以得到相反的转换结果。例如:_Object2World&&
_World2Object这就是两个互逆的矩阵
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。【Unity Shader】自定义材质面板的小技巧 - CSDN博客
【Unity Shader】自定义材质面板的小技巧
之前遇到过一些朋友问怎么在材质面板里定义类似于bool这种变量,控制一些代码的执行。我们当然可以写一个C#文件来,就像Unity为Standard Shader写材质面板一样(你可以在built-in shader包里找到这些文件),但这样有时候太麻烦了。实际上,有一种更简单的方法,不需要写额外的C#文件就可以直接在shader里定义类似bool、enum这种简单的变量,Unity为这些变量提供了内置的材质面板。而这就是通过(当然我们也可以重写内置的MaterialPropertyDrawer,但太麻烦我就不写了)。
总体来说,MaterialPropertyDrawers内置了4种最常见的面板:ToggleDrawer,EnumDrawer,KeywordEnumDrawer,PowerSliderDrawer。
ToggleDrawer
把一个类型为float的属性显示为一个开关,它的值要么是0要么是1。当选中它时,Unity还会设置一个名为大写属性名_ON(可以自定义名字)的shader feature(需要在shader里使用”#pragma shader_feature”来定义它),我们可以在shader里用过#if、#ifdef或者#if defined关键词来判断它当前是否被开启。
EnumDrawer
把一个类型为float的属性显示为一个下拉列表。可以使用UnityEngine.Rendering命名空间下的各种状态来设置对应的渲染状态,例如ZWrite、ZTest、Blend等。据实验推测,这个值不可以随便定义,老老实实用UnityEngine.Rendering。
KeywordEnum
和EnumDrawer类似,也会把一个类型为float的属性显示为一个下拉列表,但不同的是它会定义一组shader keyword(需要在shader里使用”#pragma multi_compile”来定义它们)。这些keyword的名字是大写属性名_枚举名。同样,我们可以在shader里用过#if、#ifdef或者#if defined配合#elif来判断当前选择是哪个keyword。最多同时支持9个keywords。
PowerSliderDrawer
显示一个非线性响应的滑动条,其中PowerSliderDrawer中的参数指定了底数,然后我们再根据Range()来指定范围。其实简单说来就是滑动条上的数值不再是均匀变化了,而是xslider进行变化,但我们在shader里还是可以直接访问到一个float数值。
下面的代码没有任何实际意义,只是为了演示四种面板:
Shader "Custom/Material Property Drawer Example"
Properties
[Header(Material Property Drawer Example)]
_MainTex ("Main Tex", 2D) = "white" {}
_SecondTex ("Second Tex", 2D) = "white" {}
[Space(50)]
[Toggle] _Invert ("Invert color?", Float) = 0
[Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend Mode", Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend Mode", Float) = 1
[Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 0
[Enum(pareFunction)] _ZTest ("ZTest", Float) = 0
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1
[KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
[PowerSlider(3.0)] _Shininess ("Shininess", Range (0.01, 1)) = 0.08
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Blend [_SrcBlend] [_DstBlend]
ZWrite [_ZWrite]
ZTest [_ZTest]
Cull [_Cull]
#pragma shader_feature _INVERT_ON
#pragma shader_feature ENABLE_FANCY
#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainT
float4 _MainTex_ST;
sampler2D _SecondT
float4 _SecondTex_ST;
struct appdata
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
struct v2f
float4 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
v2f vert (appdata v)
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.uv, _SecondTex);
fixed4 frag (v2f i) : SV_Target
fixed4 col = tex2D(_MainTex, i.uv.xy);
#if _INVERT_ON
#if ENABLE_FANCY
col.r = 0.5;
fixed4 secCol = tex2D(_SecondTex, i.uv.zw);
#if _OVERLAY_ADD
col += secC
#elif _OVERLAY_MULTIPLY
col *= secC
得到的面板是这样的:
上面的代码基本显示了所有可能的例子。有一点需要注意的是使用#pragma multi_compile声明多个keyword时,每个keyword之间是没有逗号的,而官网里的有逗号,这是不对的。
除了上面的四种内置属性面板外,还有一些装饰性的内置drawer,例如Space和Header。在上面的例子里,我们在它们的后面直接写属性,像下面这样:
[Space] _Prop1 ("Prop1", Float) = 0
装饰性drawer的后面有没有紧跟属性都是可以的。
shader feature和shader keyword
从上面的内容,我们知道可以使用#pragma shader_feature和#pragma multi_compile来实现“只写一份代码但实现不同功能”的目的。但世界上没有免费的午餐,Unity实际上只是在背后生成了很多份有少许不同的shader变种(shader variants),每个变种包含了不同的keyword而已,没什么非常神奇的地方。
当我们在代码里写到:
#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
Unity就生成了3个shader变种,每个对应开启其中的一个keyword。
如果我们再添加新的定义:
#pragma shader_feature ENABLE_FANCY
Unity会再分别为是否开启ENABLE_FANCY来生成两种变种,也就是一共生成了3*2=6个shader变种。因此,当我们定义了大量的feature或者keyword的时候,就会出现有大量的变种!(而且Unity允许的keyword数目也是有限制的。)
#pragma shader_feature和#pragma multi_compile的区别
那么#pragma shader_feature和#pragma multi_compile有什么区别呢?实际上,#pragma shader_feature是#pragma multi_compile的子集,#pragma shader_feature生成的变种一个是不包含任何keyword的,一个是包含某个keyword的。我们可以使用#pragma multi_compile来实现同样的目的,只需要使用一个全是下划线的名字来表示不使用任何keyword即可。下面的两句话是在大多数情况下等价的:
#pragma shader_feature ENABLE_FANCY
#pragma multi_compile __ ENABLE_FANCY
但区别在于,使用multi_compile来定义keyword的话Unity是一定会生成所有变种的,。而如果使用的是shader_feature的话,如果有些keyword我们实际上并没有使用到,Unity也不会为这些生成shader变种,我们可以在shader的代入面板里看到到底实际生成了多少个变种:
因此(非常重要!!!),shader_feature适用于那些会在材质面板中直接设置的情况,而如果需要在脚本里通过DisableKeyword和EnableKeyword来开启或关闭keyword的话就应该使用multi_compile。(栽过坑啊!!!)并且不要在#pragma后面写注释!!!如果要在脚本里通过Shader.DisableKeyword来控制keyword的开关的话,不要在Properties里写KeywordEnum,这样可能会导致脚本里的设置失效(可以重新建一个材质解决)。但如果是使用material.DisableKeyword来设置的话,就不会有这个问题,原因暂时不明。
实际上,Unity的surface shader能够有那样强大的功能也是依靠了这样的机制。也包括我们在通过使用#pragma multi_compile_fwdbase或multi_compile_fwdadd这样的代码时,我们之所以需要使用这样的语句就是因为Unity为forward pass等内置了一些shader keyword,而我们通过这些语句来让unity为不同的光照pass生成不同的shader变种,否则的话我们的shader就一种类型,只能包含特定的任务,无法为多类型的光源等进行不同的服务。
当然,我们可以独立使用#pragma shader_feature和#pragma multi_compile,而不必一定要和MaterialPropertyDrawer配合使用。我们可以直接在代码里通过Material.EnableKeyword和Material.DisableKeyword来局部开启某些keyword,也可以通过Shader.EnableKeyword和Shader.DisableKeyword来全局开启。
参考链接:
本文已收录于以下专栏:
相关文章推荐
最近在学习Shader,将学习的一些心得和案例以及基础知识罗列了一下。这一篇是unity之Shader的基础篇之基础,更多的是一些概念。该篇将分以下几个部分:
1、什么是Shader?
2、GPU和C...
又被Unity坑了一把,简单说下吧,下面都是流水账,结论就写在最后了,就是Unity5 - 5.2的shader编译机制真是不咋地。1.Why Always me?问题是这样的,我照着Unity5 的...
概要:本文主要介绍了Unity5中的标准着色器,并且也涉及到了基于物理的着色、延迟渲染等高级着色技术,而在文章后半部分,也对屏幕水幕特效的实现方法进行了讲解与分析。
依然是附上一组本文配套工程的运...
我们知道Unity4.x中,当在Asset目录中右键创建shader时默认新建的shader为unity的surface shader,而在5.x中,创建shader的选项中变成了四个shader模板...
原文地址:http://blog.csdn.net/poem_qianmo/article/details/
本篇文章中,我们学习了Unity Shader的基本写法框架,以...
Shader名字 "ShaderLab Tutorials/TestShader"{
}07-17-42.png
Shader的名字...
1、uint CreateShader(enum type) : 创建空的
type: VERTEX_SHADER,
2、void ShaderSource(ui...
面的剔除 Cull 在渲染的时候,默认情况下是只有朝向摄像机的面才会被渲染,可以告诉Unity,我想渲染哪一个朝向的面,使用Cull命令在计算体积阴影的时候会用到对Cull的操作来计算和物体相交的投影...
本文主要讲解了Unity中SurfaceShader的具体写法,以及几个常用的CG函数的用法。
在这里先说明一下,表面着色器将分为两次讲解,本文介绍表面着色器的基本概念和一些写法,用内置的兰伯特光照模...
Shader山下(二十)编译指令(Compilation Directives)介绍了如何使用编译指令,本文就专文介绍一下多重编译(multi_compile)指令。
我们可以使用multi_com...
他的最新文章
讲师:何宇健
讲师:董岩
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)【Unity Shader 合并】合并渲染状态 - 简书
【Unity Shader 合并】合并渲染状态
年关将至,整理今年的大小事务,看看哪些是可以拖到年后的。整理发现今年一直想写,却迟迟未动手写的《合并Shader》还没有开工。是的,如你所见这是在开一个新坑,不多挖点坑,将来怎么会有意愿去填坑呢!!!当然更早的《USequencer系列》也会一直更新,毕竟USequencer最大的问题还没有解决,对于这个又爱又恨的插件,我会一直跟到底。
还是先说说这个新坑吧,《合并Shader》系列旨在介绍一些技能和方法,用来把数量繁多的Shader进行减量,在保证功能不打折的情况下,精简Shader数量,原理就是把相似功能的Shader文件合并在一个文件里。当然学过这些技能后,极致情况下可以做到把所有的Shader文件合并成一个,如Unity 5.x的Standard着色器,但我不会建议你这样做,原因嘛,你尝试过后就会知道,我就不多说,因为我没有尝试过,嘿嘿!本系列对一些常用的,不常用的,正派的,歪门邪道的技能和方法都会有所涉猎。林林总总的技能和方法中,我们尽量遵从从简到繁的方式来依次遍历。
在数学中,我们学习过:把多项式中的同类项合并成一项叫做同类项的合并,也叫合并同类项。同理,提取Shader的相似部分,把多个Shader合并成一个就叫做Shader的合并,也叫合并Shader,偶尔也会引用数学的名词来称呼他为Shader的合并同类项。
Shader的合并方式方法有很多,根据不同的合并技能和方法,可以画分为不同的派系。今天优先介绍一种不太常见,但又很实用的派系,往下看。
在Shader的合并方法中,MaterialPropertyDrawer(属性定义)可以说是自成一派,但又有与其他派系有着千丝万缕、说不清道不明的关系,今天我们就先拿他来开刀。
王婆卖瓜,自卖自夸, 让我再多说几句废话。对于Shader的合并,首先让人想到的应该是宏定义,相信宏定义也是大家应用最广,最先接触到的,使用自然也不再话下,毕竟由于GPU的特殊性,Shader里常常通篇都充满了各种宏定义。当然,该系列会对宏定义有所介绍,他可是Shader合并里功高盖世的重要角色,很多地方都会有他的身影。但对他的介绍不在这一篇,也许会是下一篇,因为他还不是我认为的最简单的合并Shader方式。至于最简单的合并Shader的方式,应该是使用Unity已经预先定制好的几种MaterialPropertyDrawer来合并Shader的方式。只用修改2行代码,就可以搞定一类Shader的合并,该方法主要用来合并那些只是渲染状态不一样的Shader。
一、初次简单使用
一个完整的Shader,他的渲染状态变量有很多种,由于不是每一种状态的改变都能很明显的看到结果,而作为初次使用,优先选择一种最明显的、最容易懂的状态作为我们的测试用例,他就是ZTest,深度比较。对ZTest不太了解的朋友,可以看看官方的学习文档或者查看一下相关技术书籍,我就不在这里具体介绍这个状态了。
1.1首先我们选用的是Unity官方提供的一个最常用Shader:Normal-Diffuse.shader(Legacy Shaders/Diffuse)
1.2在属性列表(Properties)中添加一行
[Enum(pareFunction)] _ZTest ("ZTest", Float) = 2
1.3在SubShader中添加一行
ZTest [_ZTest]
1.4完整的Shader
Shader "ShaderCombine/01.ShaderCombineSimpleZTest"
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
[Enum(pareFunction)] _ZTest ("ZTest", Float) = 2
SubShader {
Tags { "RenderType"="Opaque" }
ZTest [_ZTest]
#pragma surface surf Lambert
sampler2D _MainT
struct Input {
float2 uv_MainT
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _C
o.Albedo = c.
o.Alpha = c.a;
Fallback "Legacy Shaders/VertexLit"
只用添加这两行代码,我们就可以再Inspector面板中控制使用该Shader物体的深度测试方法。1.5直观展示在Unity中的测试示例是这样的:
当然大家也可以通过这个示例测试一下每一种深度测试的方法是否与你心中所想或之前所学的是否冲突。二、再次深入使用在上面的例子中,我们只使用了深度测试。但对于我们来说,单单一个深度测试肯定满足不了我的,我们还需要更多、更多的状态,比如背面剔除、混合模式等等。在这一节中,我列举出了一些常用的状态控制量,对于一些不常用的模板什么的,就不在这里列举。当然对于没有列举的,可以依葫芦画瓢,大部分基本都是可行的。2.1一个大而全的简单示例Shader如下:
Shader "ShaderCombine/02.ShaderCombineCommonState"
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1
[Enum(Off,0,On,1)] _ZWrite ("ZWrite", Float) = 1
[Enum(pareFunction)] _ZTest ("ZTest", Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _SourceBlend ("Source Blend Mode", Float) = 2
[Enum(UnityEngine.Rendering.BlendMode)] _DestBlend ("Dest Blend Mode", Float) = 2
SubShader {
Tags { "RenderType"="Opaque" }
ZTest [_ZTest]
Cull [_Cull]
ZWrite [_ZWrite]
ZTest [_ZTest]
Blend [_SourceBlend] [_DestBlend]
#pragma surface surf Lambert
sampler2D _MainT
struct Input {
float2 uv_MainT
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _C
o.Albedo = c.
o.Alpha = c.a;
Fallback "Legacy Shaders/VertexLit"
2.2直观展示应用效果:
三、有限自定义
在上面的示例中,我们都是使用Unity预先定义好的一些枚举类型,比如pareFunction,UnityEngine.Rendering.BlendMode等。这些定义好的类型把每个状态可能的选项都一一列举了,但有时候我们并不需要这么多选项,或者说我们并不希望给美术列举出所有的可选项,毕竟很多的选项我们可能做完整个项目或者几个项目都不会使用到,而过多的选项也会带来很多的麻烦。或者换一个说法,我希望我们的功能使用起来简单易懂,不易出错并且可控,那就需要我们开发做更多的工作,去掉那些"无用"的选项。其实说这么废话,无非就是我们能不能自己定义每个状态的选项呢?答案当然是可以的。
在给出自定义方式前,我们先来熟悉一下Unity给我们提供的这几个枚举类型。3.1 剔除模式
UnityEngine.Rendering.CullMode:
public enum CullMode
//Disable culling.
Front = 1,
//Cull front-facing geometry.
//Cull back-facing geometry.
3.2 比较方式该比较方式通用与深度比较和模板比较
pareFunction
//Depth or stencil comparison function.
public enum CompareFunction
//Depth or stencil test is disabled.
//Never pass depth or stencil test.
//Pass depth or stencil test when new value is less than old one.
//Pass depth or stencil test when values are equal.
//Pass depth or stencil test when new value is less or equal than old one.
//Pass depth or stencil test when new value is greater than old one.
//Pass depth or stencil test when values are different.
GreaterEqual = 7,
//Pass depth or stencil test when new value is greater or equal than old one.
//Always pass depth or stencil test.
3.3 混合模式
UnityEngine.Rendering.BlendMode
//Blend mode for controlling the blending.
public enum BlendMode
//Blend factor is (0, 0, 0, 0).
//Blend factor is (1, 1, 1, 1).
//Blend factor is (Rd, Gd, Bd, Ad).
//Blend factor is (Rs, Gs, Bs, As).
OneMinusDstColor = 4,
//Blend factor is (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad).
//Blend factor is (As, As, As, As).
OneMinusSrcColor = 6,
//Blend factor is (1 - Rs, 1 - Gs, 1 - Bs, 1 - As).
//Blend factor is (Ad, Ad, Ad, Ad).
OneMinusDstAlpha = 8,
//Blend factor is (1 - Ad, 1 - Ad, 1 - Ad, 1 - Ad).
SrcAlphaSaturate = 9,
//Blend factor is (f, f, f, 1); where f = min(As, 1 - Ad).
OneMinusSrcAlpha = 10
//Blend factor is (1 - As, 1 - As, 1 - As, 1 - As).
3.4 有限的自定义在上面3个小小节中,我们了解了Unity自身提供的状态选项,而且每一个状态选项后都强制赋上了相应的数值,这是有原因的。因为我们写好的Shader不管怎样都要首先经过Unity的编译等处理转换为目标平台的着色器语言。而Unity自己的Shader编译器我们是没法修改(没有源码的情况),也就是说我们不能随意更改这些状态的数值。其实我们修改的这些状态值都是给Unity的Shader编译器看的,而编译器对状态的数值是理解是固化好的。SO,虽然我们可以自定义这些状态选项,但也不会任由我们随意定义,这就好比戴着镣铐跳舞,虽然有限制,但我们依然可以跳出优美的舞蹈。自定义非常的简单,我们可以减少选项的数量,但是不能改变每一项的值,这就要求我们强行给每一个值赋上对应的值,依然还是用深度测试实验,如下所示:这是之前的:[Enum(pareFunction)] _ZTest ("ZTest", Float) = 2这是自定后的:[Enum(Less,2,Greater,5)] _ZTest ("ZTest", Float) = 2选项与数值全部使用逗号分隔,该示例中我只给出了两个选项,小于和大于,便于直观查看。完整Shader如下:
Shader "ShaderCombine/03.ShaderCombineCustomState"
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
[Enum(Less,2,Greater,5)] _ZTest ("ZTest", Float) = 2
SubShader {
Tags { "RenderType"="Opaque"}
ZTest [_ZTest]
#pragma surface surf Lambert
sampler2D _MainT
struct Input {
float2 uv_MainT
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _C
o.Albedo = c.
o.Alpha = c.a;
Fallback "Legacy Shaders/VertexLit"
3.5 直观展示在Unity中的样子是这样的:
细心的读者可能已经发现我们在第二章节中的完整示例中就有使用自定义,就是里面的那个写深度ZWrite选项,因为没有在Unity里面找到相应的枚举值,就直接使用了自定义,反正只要保证数值是正确的就可以任意发挥使用。更多的使用和应用场景就等你们去发现了,我这只是抛砖引玉。
以上便是MaterialPropertyDrawer的应用场景之一,对于MaterialPropertyDrawer的应用,在后续的篇章中也还会陆续出现。我计划把MaterialPropertyDrawer应用当成《合并Shader》系列中的一个分支,当然《合并Shader》系列不会仅且只有这一个分支的,O(∩∩)O哈哈~
其实最初是想花一整章篇幅来讲解MaterialPropertyDrawer的各种使用,但其内部的扩展空间还比较广泛。写下来篇幅太长,而太长的篇幅,阅读起来也比较麻烦,所以还是拆成几章篇幅来慢慢絮叨吧,同时也遵从一次只讲一个问题。后注:本文首发于侑虎公众号,后同步更新在本人自己的博客 以及其他博客网站,转载请注明出处unity2D 使用shader创建缩放时重复原始大小的循环图像 - CSDN博客
unity2D 使用shader创建缩放时重复原始大小的循环图像
在制作游戏时,经常需要用到将较小的重复循环的纹理图像拼成一个大图,比如地面上的尖刺,或是墙面砖块背景。遇到这种问题时通常的处理手段是像tilemap那样用单张纹理图片作为一个tile(unity中一般用sprite),将多个tile拼接起来形成一张大图。
例如有一张256x256的无缝墙面纹理,我希望用这张图铺满一个512x512大小的墙面, 512x512的墙面正好需要4个256x256的tile来拼接,拼接完成时如图1所示。
但是这样处理要求拼接后的图像大小的宽和高需要分别为原图宽和高的整数倍,这样比较好处理。为了尽量满足这个要求,通常会将用作tile的图片做的非常小,这样可以满足尽量多的拼接图片不同大小的情况。
有没有一种更灵活的方式来处理这种问题,只用一个sprite就可以做出任意大小的墙面呢?
那必须有啊!
其实我们可以借助shader来完成这样一个效果。在opengl中有一个参数叫做GL_TEXTURE_WRAP,就是在纹理超出边界怎么处理。有一种模式是GL_REPEAT,就是可以将纹理进行重复。受到这个启发,我们是不是也可以利用这种重复模式,来实现缩放sprite时,纹理的大小不变,多出的部分则自动用重复的纹理进行铺满呢?
当然可以!只不过会麻烦一些......
unity中,纹理有一个wrap mode属性,可以设置成repeat或是clamp,其中repeat就是我们想要的重复模式。但是unity2d会自动将导入的纹理转换成sprite类型纹理,在sprite类型纹理的属性中,我们无法调整纹理的wrap mode属性所以我们首先需要将导入的纹理变成texture类型,如图2所示。修改完之后记得点apply进行保存。
之后我们在unity编辑器新建一个sprite,这是我们发现新建的sprite不能直接使用我们修改的纹理了,所以我们需要通过脚本来用纹理生成一个sprite对象,赋给新建的sprite。像这样:
&div style=&text-align:&&&span style=&font-family: Arial, Helvetica, sans-&&&span style=&white-space:pre&& &/span&// 将你的纹理&span style=&font-size: 18 white-space: background-color: rgb(240, 240, 240);&&YourTextureForSprite变成sprite&/span&&/span&&/div&&div style=&text-align:&&&span style=&font-family: Arial, Helvetica, sans-&&&span style=&white-space:pre&& &/span&Sprite sprite = Sprite.Create((Texture2D)YourTextureForSprite, new Rect(0, 0, &span style=&font-size: 18 white-space: background-color: rgb(240, 240, 240);&&YourTextureForSprite&/span&.width, &span style=&font-size: 18 white-space: background-color: rgb(240, 240, 240);&&YourTextureForSprite&/span&.height), new Vector2(0.5f, 0.5f));&/span&&/div&&div style=&text-align:&&&/div&&div style=&text-align:&&&/div&&div style=&text-align:&&&span style=&font-family: Arial, Helvetica, sans-&& &span style=&white-space:pre&& &/span&GetComponent&SpriteRenderer&().sprite =&/span&&/div&
有了纹理,下一步我们希望sprite在缩放时并不改变原始纹理的大小,而是将纹理进行重复。默认的shader是无法完成这项工作的,这时候就需要我们自己去写一个shader了。我们需要将sprite的scale值告诉shader,以便进行处理。这里我将unity自带的默认的sprite
shader改写了一下,代码如下:
Shader &Custom/RepeatShader&
&span style=&white-space:pre&& &/span&Properties
&span style=&white-space:pre&& &/span&{
&span style=&white-space:pre&&
&/span&[PerRendererData] _MainTex(&Sprite Texture&, 2D) = &white& {}
&span style=&white-space:pre&&
&/span&_Color(&Tint&, Color) = (1,1,1,1)
&span style=&white-space:pre&&
&/span&[MaterialToggle] PixelSnap(&Pixel snap&, Float) = 0
&span style=&white-space:pre&&
&/span&_ScaleX(&ScaleX&, Float) = 1
&span style=&white-space:pre&&
&/span&_ScaleY(&ScaleY&, Float) = 1
&span style=&white-space:pre&& &/span&}
&span style=&white-space:pre&& &/span&SubShader
&span style=&white-space:pre&& &/span&{
&span style=&white-space:pre&&
&/span&Tags
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&&Queue& = &Transparent&
&span style=&white-space:pre&&
&/span&&IgnoreProjector& = &True&
&span style=&white-space:pre&&
&/span&&RenderType& = &Transparent&
&span style=&white-space:pre&&
&/span&&PreviewType& = &Plane&
&span style=&white-space:pre&&
&/span&&CanUseSpriteAtlas& = &True&
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&Cull Off
&span style=&white-space:pre&&
&/span&Lighting Off
&span style=&white-space:pre&&
&/span&ZWrite Off
&span style=&white-space:pre&&
&/span&Fog{ Mode Off }
&span style=&white-space:pre&&
&/span&Blend One OneMinusSrcAlpha
&span style=&white-space:pre&&
&/span&Pass
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&CGPROGRAM
&span style=&white-space:pre&&
&/span&#pragma vertex vert
&span style=&white-space:pre&&
&/span&#pragma fragment frag
&span style=&white-space:pre&&
&/span&#pragma multi_compile DUMMY PIXELSNAP_ON
&span style=&white-space:pre&&
&/span&#include &UnityCG.cginc&
&span style=&white-space:pre&&
&/span&struct appdata_t
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&float4 vertex : POSITION;
&span style=&white-space:pre&&
&/span&float4 color : COLOR;
&span style=&white-space:pre&&
&/span&float2 texcoord : TEXCOORD0;
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&struct v2f
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&float4 vertex : SV_POSITION;
&span style=&white-space:pre&&
&/span&fixed4 color : COLOR;
&span style=&white-space:pre&&
&/span&half2 texcoord : TEXCOORD0;
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&fixed4 _C
&span style=&white-space:pre&&
&/span&fixed _ScaleX;
&span style=&white-space:pre&&
&/span&fixed _ScaleY;
&span style=&white-space:pre&&
&/span&v2f vert(appdata_t IN)
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&v2f OUT;
&span style=&white-space:pre&&
&/span&OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
&span style=&white-space:pre&&
&/span&OUT.texcoord = IN.
&span style=&white-space:pre&&
&/span&OUT.color = IN.color * _C
&span style=&white-space:pre&&
&/span&#ifdef PIXELSNAP_ON
&span style=&white-space:pre&&
&/span&OUT.vertex = UnityPixelSnap(OUT.vertex);
&span style=&white-space:pre&&
&/span&#endif
&span style=&white-space:pre&&
&/span&return OUT;
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&sampler2D _MainT
&span style=&white-space:pre&&
&/span&fixed4 frag(v2f IN) : SV_Target
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&IN.texcoord.x *= _ScaleX;
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&IN.texcoord.y *= _ScaleY;
&span style=&white-space:pre&&
&/span&fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.
&span style=&white-space:pre&&
&/span&c.rgb *= c.a;
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&span style=&white-space:pre&&
&/span&ENDCG
&span style=&white-space:pre&&
&span style=&white-space:pre&& &/span&}
如果你有unity shader的基础的话应该不难看懂,没有的话也不要紧,这里我只是在原来的基础上新加了两个可以从脚本传进来的变量_ScaleX和_ScaleY,分别是sprite X轴和Y轴的缩放值。然后做的工作只是在fragment shader中将纹理坐标的x和y值分别乘上这两个值,仅此而已。
这里简单解释一下,纹理坐标(x,y)是两个0到1的值,与纹理的实际大小无关,(0,0)代表纹理的左下角,(1,1)代表纹理的右上角,其实可以理解成该位置与纹理宽高所对应比例。举个例子,假设是一个1x2的纹理,shader中纹理坐标(0,0)代表纹理中(0,0)的像素位置,shader中纹理坐标(1,1)代表纹理中(1,2)的像素位置。
当sprite被缩放时,取到纹理坐标仍然是是两个0到1的值不变,所以这里将纹理坐标乘上缩放值,实际上就是将缩放后绘制出来的纹理再放缩回去,还原成原来的大小。但是多出来部分怎么办呢?比如纹理坐标(0.6,0.7)都乘上2,变成了(1.2,1.4),两个值都大于1了,shader会怎么处理呢?这里由于纹理的wrap
mode是repeat,shader就会自动取超出来的部分重新作为纹理坐标来处理,从而使纹理重复出来,(1.2,1.4)两个值都减去取值范围最大值1,就变成了(0.2,0.4)成为了新的纹理坐标。
有了shader,我们自然要新建一个材质,并将shader选为我们自定义的shader。如图3:
纹理和材质都有了,下一步就该完成我们的脚本了,同纹理一样,我们也需要将material赋给SpriteRenderer组件。
这里有一个问题,我们不能在属性面板里直接给SpriteRenderer组件赋值,而需要在脚本里实例化一个材质赋给SpriteRenderer组件。为什么呢?因为unity默认情况下是多个sprite共享同一个material的,如果使用该material的对象有多个,那么当其中一个改变的material中的变量时,其他的对象中的material也会改变。这当然不是我们想要看到的,对吧?所以我们需要实例化material,相当于新建一个同样的material赋给每个使用该material的对象,这样就互不影响了。(注意使用下面的脚本时需要现将SpriteRenderer在属性面板中的material置空,当然你也可以修改脚本......)
最后脚本的完整代码如下:
&div style=&text-align:&&using UnityE
using System.C
public class RepeatSprite : MonoBehaviour {
    // 用作tile的纹理
    public Texture TextureForS
    // 纹理所使用的自定义材质
    public Material MaterialO
    // 初始缩放
    public Vector3 scale = Vector3.
    // SpriteRenderer组件
    SpriteRenderer spriteR
    // Use this for initialization
    public void Start()
    {
        transform.localScale =
        spriteRenderer = GetComponent&SpriteRenderer&();
        // 若tile纹理不为空,则使用tile纹理创建相应的sprite纹理赋给SpriteRenderer组件
        if (TextureForSprite != null)
        {
            Sprite sprite = Sprite.Create((Texture2D)TextureForSprite, new Rect(0, 0, TextureForSprite.width, TextureForSprite.height), new Vector2(0.5f, 0.5f));
            spriteRenderer.sprite =
        }
        // 若自定义使用的材质不为空,则实例化一个材质赋给SpriteRenderer组件
        if (MaterialOrigin != null)
        {
            spriteRenderer.sharedMaterial = Instantiate&Material&(MaterialOrigin);
        }
        // 若自定义材质创建成功,则设置shader获取的缩放值为sprite的缩放值
        if (spriteRenderer.sharedMaterial)
        {
            spriteRenderer.sharedMaterial.SetFloat(&_ScaleX&, transform.lossyScale.x);
            spriteRenderer.sharedMaterial.SetFloat(&_ScaleY&, transform.lossyScale.y);
        }
    }
#if UNITY_EDITOR
    // 当在编辑器中改变脚本中的缩放值时,同时改变shader获取的缩放值和sprite的大小
    void OnValidate()
    {
        // 在编辑器中做同Start()初始化时相同的工作
        if (spriteRenderer == null)
        {
            spriteRenderer = GetComponent&SpriteRenderer&();
        }
        if (TextureForSprite != null && spriteRenderer.sprite == null)
        {
            Sprite sprite = Sprite.Create((Texture2D)TextureForSprite, new Rect(0, 0, TextureForSprite.width, TextureForSprite.height), new Vector2(0.5f, 0.5f));
            spriteRenderer.sprite =
        }
        if (MaterialOrigin != null && spriteRenderer.sharedMaterial == null)
        {
            spriteRenderer.sharedMaterial = Instantiate&Material&(MaterialOrigin);
        }
        // 改变shader获取的缩放值和sprite的大小为脚本设置的缩放值
        if (transform.localScale != scale)
        {
            transform.localScale =
            if (spriteRenderer.sharedMaterial)
            {
                spriteRenderer.sharedMaterial.SetFloat(&_ScaleX&, transform.lossyScale.x);
                spriteRenderer.sharedMaterial.SetFloat(&_ScaleY&, transform.lossyScale.y);
            }
        }
    }
}&span style=&font-size:18&&
&/span&&/div&
最后注意,我们加上脚本之后,只能够通过修改脚本的缩放值才能获得我们想要的结果,在transform组件中修改是无效的哦!下面放出一张效果图:
本文已收录于以下专栏:
相关文章推荐
在我们找资源的时候,有时候会遇到那种把一堆图片放进一张png图片里面的情况,在做2D游戏的时候,我们往往需要将里面的一块块小图片切割出来使用,那么问题就来了,怎么切割呢,现在我们提供一张小图片,给大家...
Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight)
Texture2D ...
第一次用 Unity2D ,拖了一个 Sprite到场景中,发现问题,640 宽度的图片,却不能占满 640x960 分辨率的屏幕。
到Momo的博客看到相关的文章
http://www....
sprite的尺寸获取、场景内物体的获取
UGUI与相机配合调整设计尺寸并进行屏幕适配,适合初学者学习
unity3d 更换sprite renderer中sprite的图片
所更换图片尺寸与原图片相同
shader编写是在学习.cn/s/blog_d8zf.html之后为了练手而制作。需要编写一个使贴图能够随着时间缩放的shader,选...
教程8缩放变换原文: http://ogldev.atspace.co.uk/www/tutorial08/tutorial08.html背景缩放变换非常简单,它的目的是增大或者缩小物体的尺寸。比如你...
首先,我们先实现用代码在shader里面实现缩放,平移和旋转!
上代码,首先是我们的shader:
Shader "Unlit/贴图的平移缩放"
Properties
_MainTex("Te...
用法很简单,把这个脚本放在有贴图的gameobject上就可以了
贴图最好用psd格式的,jpg格式的不行
#pragma strict
// Scroll main textur...
他的最新文章
讲师:何宇健
讲师:董岩
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 unity shader 的文章

 

随机推荐