🎆
Unity 背景を画像にする
Summary
Unityで背景を一枚の画像にする方法のメモです。
開発環境
Unity 2021.3.6f1
Windows 10
この記事の更新履歴
2022-11-13:コード修正
2022-11-12:新規作成
RendererFeature の追加
テクスチャを描画するRendererFeatureを追加します。
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class BackGroundRendererFeature : ScriptableRendererFeature
{
class CustomRenderPass : ScriptableRenderPass
{
Material material;
Mesh mesh;
public Texture mainTexture;
public Color mainColor = Color.white;
public float offsetU = 0;
public float offsetV = 0;
public float scaleU = 1.0f;
public float scaleV = 1.0f;
public CustomRenderPass(RenderPassEvent renderPassEvent)
{
this.material = new Material(Shader.Find("Hidden/BackGround"));
this.renderPassEvent = renderPassEvent;
this.profilingSampler = new ProfilingSampler("BackGroundRenderer");
// 描画するメッシュを作成します。
// このメッシュは画面全体を覆うものですが、位置調整はシェーダー側でやっているので頂点位置に意味はありません。
mesh = new Mesh();
mesh.vertices = new Vector3[]
{
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(0, 1f, 0),
new Vector3(1f, 1f, 0),
};
mesh.uv = new Vector2[]
{
new Vector2(0, 1f),
new Vector2(1f, 1f),
new Vector2(0, 0f),
new Vector2(1f, 0f),
};
mesh.triangles = new int[]
{
0, 1, 2,
1, 2, 3,
};
}
// Here you can implement the rendering logic.
// Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
// You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// Setting Material
material.SetTexture("_MainTex", mainTexture);
material.SetColor("_MainColor", mainColor);
material.SetVector("_MainTex_ST", new Vector4(scaleU, scaleV, offsetU, offsetV));
if(renderingData.cameraData.camera.aspect > 1.0f)
{
material.EnableKeyword("_ASPECTBASIS_HORIAZONTAL");
material.DisableKeyword("_ASPECTBASIS_VERTICAL");
}
else
{
material.EnableKeyword("_ASPECTBASIS_VERTICAL");
material.DisableKeyword("_ASPECTBASIS_HORIAZONTAL");
}
var cmd = CommandBufferPool.Get();
using(new ProfilingScope(cmd, profilingSampler))
{
cmd.DrawMesh(mesh, Matrix4x4.identity, material);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
public Texture mainTexture;
public Color mainColor = Color.white;
public float offsetU = 0;
public float offsetV = 0;
public float scaleU = 1.0f;
public float scaleV = 1.0f;
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
CustomRenderPass m_ScriptablePass;
/// <inheritdoc/>
public override void Create()
{
m_ScriptablePass = new CustomRenderPass(renderPassEvent);
}
// Here you can inject one or multiple render passes in the renderer.
// This method is called when setting up the renderer once per-camera.
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
m_ScriptablePass.mainTexture = mainTexture;
m_ScriptablePass.mainColor = mainColor;
m_ScriptablePass.offsetU = offsetU;
m_ScriptablePass.offsetV = offsetV;
m_ScriptablePass.scaleU = scaleU;
m_ScriptablePass.scaleV = scaleV;
renderer.EnqueuePass(m_ScriptablePass);
}
}
こちらのRendererFeatureをRendererに追加すると以下のようになります。
Shader を作成する
Shaderは以下の通り
Shader "Hidden/BackGround"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MainColor("Color", Color) = (1,1,1,1)
[KeywordEnum(HORIAZONTAL, VERTICAL)]_AspectBasis("Aspect basis", Int) = 0
}
SubShader
{
Tags { "Queue" = "Background" "RenderType" = "Background" "RenderPipeline" = "UniversalPipeline" }
// No culling or depth
Cull Off
ZWrite Off
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _ASPECTBASIS_HORIAZONTAL _ASPECTBASIS_VERTICAL
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
TEXTURE2D(_MainTex);
float4 _MainTex_ST;
SAMPLER(sampler_MainTex);
half4 _MainColor;
struct Attributes
{
float4 positionOS : POSITION;
uint vertexID : SV_VertexID;
float2 texcoord0 : TEXCOORD0;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
Varyings vert (Attributes IN)
{
Varyings OUTPUT;
// copy from "com.unity.render-pipelines.core/Runtime/Utilityes/Blit.hlsl"
#if SHADER_API_GLES
float4 pos = IN.positionOS;
float2 uv = IN.texcoord0;
#else
float4 pos = GetFullScreenTriangleVertexPosition(IN.vertexID, UNITY_RAW_FAR_CLIP_VALUE);
float2 uv = GetFullScreenTriangleTexCoord(IN.vertexID);
#endif
#ifdef _ASPECTBASIS_HORIAZONTAL
float aspect = (_ScreenParams.y / _ScreenParams.x);
uv.y = (uv.y * aspect) + (abs(aspect - 1) * 0.5);
#elif _ASPECTBASIS_VERTICAL
float aspect = (_ScreenParams.x / _ScreenParams.y);
uv.x = (uv.x * aspect) + (abs(aspect - 1) * 0.5);
#endif
OUTPUT.positionHCS = pos;
OUTPUT.uv = TRANSFORM_TEX(uv, _MainTex);
return OUTPUT;
}
half4 frag (Varyings IN) : SV_Target
{
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
col *= _MainColor;
return col;
}
ENDHLSL
}
}
}
vert 関数でメッシュが画面全体を覆うように位置調整しているところが重要な点です。詳しくはcom.unity.render-pipelines.core/ShaderLibrary/Common.hlslを参照してください。
Vertex Index から頂点位置を計算しているところ
GetFullScreenTriangleVertexPosition
はVertex Index から画面全体を覆うような頂点位置を算出する関数です。実装詳細はこちら。
UNITY_RAW_FAR_CLIP_VALUE
はクリッピングされた領域の最遠値を表しています。
Discussion