需求:
将模型作为像素格,铺满整个屏幕。并在物体上显示图像。
分析:
- 按照屏幕分辨率布满物体。(最终效果无关相机模式,但相机按照正交模式来设置,使创建的物体刚好作为屏幕)
- 所有物体共用同一个shader,同一个材质,降低消耗。
- 物体世界坐标映射到uv进行采样
功能点:
- 无论分辨率如何,在相机正交模式下,size=0.5时,视口的y长度即为unity的1个单位(放一个cube,cube的高刚好充满视口的高度)
- 多物体共用材质,改变单个物体材质参数,会生成材质实例;这里使用世界坐标的特殊性,避免生成材质实例
- 坐标重映射
效果:
代码
C#生成物体:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreateCubeScreen : MonoBehaviour
{
//屏幕中心的cube位置(0,1,0)
//当相机为正交模式时 0.5的size 屏幕高度就是1cube
public int xCount;
public int yCount;
public float space;
Vector3 centerPos = Vector3.zero;
public GameObject prefabObj;
public Transform objParent;
GameObject obj;
// Start is called before the first frame update
void Start()
{
//根据分辨率自动改x数量
if(xCount == 0)
{
xCount = yCount * Screen.width / Screen.height;
}
Vector3 zeroPos = centerPos - new Vector3((1 + space) * (xCount / 2), (1 + space) * (yCount / 2), 0);
for (int y = 0; y < yCount; y++)
{
for(int x = 0; x < xCount; x++)
{
obj = Instantiate(prefabObj, objParent);
obj.transform.position = zeroPos;
//zeroPos.x++;
zeroPos.x += 1+space;
//多材质实例
//Color xx = new Color(Random.Range(0,255)/255f, Random.Range(0, 255)/255f, Random.Range(0, 255)/255f);
//obj.GetComponent<MeshRenderer>().material.SetColor("_EmissionColor", xx);
}
zeroPos.y += 1+space;
zeroPos.x = centerPos.x - (1 + space) *( xCount / 2);
}
//计算相机位置
float yspaceCount = yCount - 1;
float yspacelength = yspaceCount * space;
// float cubesize = Mathf.Ceil(yspacelength);
float cameraSize = (yCount + yspacelength) * 0.5f;
Camera.main.orthographicSize = cameraSize;
float addY = 0, addX = 0;
if (yCount % 2 == 0)
addY = Camera.main.transform.position.y - 0.5f - space / 2;
if (xCount % 2 == 0)
addX = Camera.main.transform.position.x - 0.5f - space / 2;
Camera.main.transform.position = new Vector3(addX,addY,-10);
}
}
shader:
Shader "Unlit/AllCubeUse"
{
//所有cube使用同一个材质
//根据xy格子数,和space参数 构造uv图
//根据模型原点转换到世界坐标来决定在uv里的位置
//奇数 0点距离为:(yCount-1)/2 * (1+space) + 0.5*1
//偶数 0点距离为:(yCount/2 *1 + (yCount/2 - 1)*space + 0.5*space
Properties
{
_MainTex ("Texture", 2D) = "white" {
}
_MapProperties("WorldPos Map",Vector)=(0,0,0,0)
_Baohedu("baohe",Range(0,2))=1
}
SubShader
{
Tags {
"RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#define space _MapProperties.z
#define xCount _MapProperties.x
#define yCount _MapProperties.y
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 centerWorldPos:TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MapProperties;
float _Baohedu;
float Remap(float a,float b,float c,float d,float val){
return (val-a)/(b-a) * (d-c) + c;
}
float rand(float3 z){
return frac(sin(dot(z.xyz,float3(12.9898,78.233,53.539))) * 43758.5453);
}
fixed3 luminance(fixed3 colorpixel){
fixed lumin = 0.2125 * colorpixel.r + 0.7154 * colorpixel.g + 0.0721 * colorpixel.b;
return fixed3(lumin,lumin,lumin);
}
//计算map函数
float2 newUV(float3 worldPos){
float minX = -(1+space)*(xCount/2);
float maxX = minX + (xCount-1)*(1+space);
float minY = -(1 + space) * (yCount / 2);
float maxY = minY + (yCount-1)*(1+space);
float newX = Remap(minX,maxX,0,1,worldPos.x);
float newY = Remap(minY,maxY,0,1,worldPos.y);
return float2(newX,newY);
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
//模型原点作为uv依据
o.centerWorldPos = mul(unity_ObjectToWorld,float4(0,0,0,1));
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//uv映射:模型原点在世界坐标的位置&网格世界Map -->01uv
float2 uvtemp = newUV(i.centerWorldPos);
// sample the texture
fixed4 col = tex2D(_MainTex, uvtemp);
col.xyz = lerp(luminance(col),col,_Baohedu);
float randV = rand(i.centerWorldPos);
float addT = 0.5*sin(_Time.y*randV*2 +randV );
col *= 1+ addT *randV;
//col = fixed4(0.1,0.2,0.3,1) * i.centerWorldPos.x;
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}