想象一个场景,其中有十个相同的简单“立方体”。
它们每个都具有相同的材质“ M1”,该材质具有简单的标准着色器以及作为纹理的简单PNG。
如果要转到素材上并调整平铺,
您可以方便地翻转纹理以获得不同的外观。
请注意,当然,这会同时更改所有十个多维数据集。
是否可以修改标准着色器,以便简单地
在每个使用材质的对象上
着色器随机更改平铺(和偏移量)
我的意思是基于“每个对象” ;在示例中,十个每个将随机不同。
(因此:一个立方体将显示-1,1瓦片,一个立方体将显示-1,-1瓦片,依此类推...尽管每个立方体都使用 same ,仅存在一种材料。)
注意,
(1)生成材料的几个不同版本,每个版本具有不同的平铺,并为每个多维数据集随机选择一个版本,这是很简单的。那不是要走的路
(2)请注意,如果您更改一种材料,则它当然会制作多个副本。你不可能有成千上万的材料。
解决方案是在一个着色器中拥有一个着色器,该着色器知道(随机地)偏移量,该着色器位于正在处理的每个对象上。(即,针对每个特定对象)基础)
这就是我在这里问的问题。
答案 0 :(得分:8)
我注意到您真的很想仅使用着色器来执行此操作。您不能仅使用着色器来执行此操作。问题是无法在着色器中生成随机数。 问题是只能做一次。我还没有找到一种方法,也不认为可以。
这是一个问题,应该使用C#端的代码来解决。
(1)生成几个不同版本的 材料,每个都有不同的拼贴,并随机选择其中之一 对于每个立方体。那不是要走的路
(2)请注意,如果您改变一种材料,那么它当然会制造出不止一种材料 复制。你不可能有成千上万的材料。
完全不是问题。这就是MaterialPropertyBlock
的用途。它允许您修改着色器属性,而无需创建该材质的新实例。
“在要绘制具有相同材质但属性稍有不同的多个对象的情况下使用它。”
MaterialPropertyBlock
以下代码将导致创建材料的许多实例:
void Start()
{
MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
int tileX = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
int tileY = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
Vector2 tile = new Vector2(tileX, tileY);
meshRenderer.material.SetTextureScale("_MainTex", tile);
}
使用MaterialPropertyBlock
解决了此问题。没有复制材料。由于您关心性能,因此还应该使用Shader.PropertyToID
:
void Start()
{
int propertyID = Shader.PropertyToID("_MainTex_ST");
meshRenderer = gameObject.GetComponent<MeshRenderer>();
int tileX = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
int tileY = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
Vector2 tile = new Vector2(tileX, tileY);
MaterialPropertyBlock matPropBlock = new MaterialPropertyBlock();
//Get the current MaterialPropertyBlock settings
meshRenderer.GetPropertyBlock(matPropBlock);
//Assign the new tile value
matPropBlock.SetVector(propertyID, tile);
//matPropBlock.SetVector(Shader.PropertyToID("_MainTex_ST"), tile);
//Apply the modified MaterialPropertyBlock back to the renderer
meshRenderer.SetPropertyBlock(matPropBlock);
}
如果在游戏中完成一次,则将脚本附加到每个GameObject上实际上没有任何意义。只需将它附加到一个空的GameObject上,按标记查找所有对象,然后在每个对象上运行代码。
void Start()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("YourObjTag");
int propertyID = Shader.PropertyToID("_MainTex_ST");
for (int i = 0; i < objs.Length; i++)
{
MeshRenderer meshRenderer = objs[i].GetComponent<MeshRenderer>();
int tileX = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
int tileY = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
Vector2 tile = new Vector2(tileX, tileY);
MaterialPropertyBlock matPropBlock = new MaterialPropertyBlock();
//Get the current MaterialPropertyBlock settings
meshRenderer.GetPropertyBlock(matPropBlock);
//Assign the new tile value
matPropBlock.SetVector(propertyID, tile);
//Apply the modified MaterialPropertyBlock back to the renderer
meshRenderer.SetPropertyBlock(matPropBlock);
}
}
答案 1 :(得分:5)
Shader "RandomFlip"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" "DisableBatching"="True" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
// position of pivot in world space
float3 pivotWorldPos = float3( unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w );
// randomness achieved by feeding trigonometry function with large numbers
float flipHorizontally = sin( ( pivotWorldPos.x + pivotWorldPos.y + pivotWorldPos.z ) * 1000 ) > 0;
float flipVertically = cos( ( pivotWorldPos.x + pivotWorldPos.y + pivotWorldPos.z ) * 1000 ) > 0;
// randomly flipping uvs
float2 uv = lerp( v.uv, float2( 1.0 - v.uv.x, v.uv.y ), flipVertically );
uv = lerp( uv, float2( uv.x, 1.0 - uv.y ), flipHorizontally );
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
尽管最好不要移动这些对象:D因为这里的随机性来自对象在世界空间中的位置。批处理还会破坏随机性。
答案 2 :(得分:4)
如果要翻转,则对那些对象使用alpha = 0,对其他对象使用alpha = 1。
在着色器的顶点数据中编写如下内容:
半翻转= 1 -2 * vertex.a;
因此翻转将为-1或1。
从评论中:
但是请注意,每次访问Unity中的.mesh
时,您都将创建一个全新的网格:
public class TestFlipUV : MonoBehaviour
{
private void Awake()
{
Mesh mesh = GetComponent<MeshFilter>().mesh;
// NOTE. IN UNITY, ACCESSING .mesh CREATES
// A NEW INSTANCE OF THAT MESH EVERY TIME
Vector2[] uv2 = mesh.uv;
float rnd = Random.RandomRange(0.0f, 1.0f);
if (rnd > 0.5f)
{
for (int i = 0; i < uv2.Length; i++)
{
uv2[i].x *= -1;
}
}
rnd = Random.RandomRange(0.0f, 1.0f);
if (rnd > 0.5f)
{
for (int i = 0; i < uv2.Length; i++)
{
uv2[i].y *= -1;
}
}
mesh.uv2 = uv2;
}
}
...然后...
Shader "Unlit/FlipUV"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.uv *= v.uv2;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}
答案 3 :(得分:0)
我将其发布为答案,您可以尝试一下,看看它是否对您有用。 (如果没有,也许其他人会发现此解决方案很有帮助)。可以使用SetTextureScale
为每个多维数据集附加
public class MatTilingChange : MonoBehaviour {
MeshRenderer mr;
int[] vals = new int[] { -1, 1 };
private void Awake()
{
mr = GetComponent<MeshRenderer>();
}
void Start () {
mr.material.SetTextureScale("_MainTex", new Vector2(vals[Random.Range(0, 2)],
vals[Random.Range(0, 2)]));
}
}