Context
I am writing a ScriptableImporter for a specialised image format (the specifics are not relevant). To do so, I need to semi-procedurally generate a Texture2D. My idea was to create a RenderTexture at import time, and call Graphics.Blit(source, destination, material) on it using a material whose shader that does the procedural part of the job.
I acknowledge this might be a bit of an XY problem, so I will also accept an answer that generally allows me to render a shader to a texture of a specified size, which is the actual use case.
I am using Unity 2022.2.19 and Universal Render Pipeline 14.0.7.
The problem
However, I don't seem to be able to use this technique. I created a trivial shader that simply outputs white everywhere, and called Blit with it, yet the texture is still completely black.
The code
Test shader
Shader "Hidden/My Cool Shader"
{
Properties
{
_MainTex("InputTex", 2D) = "white" {}
}
SubShader
{
Blend One Zero
Pass
{
Name "Blit"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
sampler2D _MainTex;
struct appdata {
float4 uv0: TEXCOORD0;
};
struct v2f {
float4 uv0: TEXCOORD0;
};
v2f vert(appdata vertex) {
v2f fragment;
fragment.uv0 = vertex.uv0;
return fragment;
}
float4 frag(v2f IN) : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
Double-buffered helper
This is so I can render multiple times to the "same" texture, as I might
internal struct DoubleBufferedRenderTexture : IDisposable
{
private RenderTexture _a;
private RenderTexture _b;
public DoubleBufferedRenderTexture(CustomRenderTexture source)
{
_a = source;
_b = Object.Instantiate(source);
}
public void Blit(Material material)
{
Graphics.Blit(_a, _b, material);
(_a, _b) = (_b, _a);
}
public RenderTexture Consume()
{
var result = _a;
_a = null;
return result;
}
public void Dispose()
{
Object.DestroyImmediate(_a);
Object.DestroyImmediate(_b);
}
}
The Blitting
var canvas = new DoubleBufferedRenderTexture(new RenderTexture(64, 64, GraphicsFormat.R32G32B32A32_SFloat, GraphicsFormat.None));
var brush = new Material(Shader.Find("Hidden/My Cool Shader"));
// Here I would set up the material properties when using an actual shader.
canvas.Blit(brush);
ctx.AddObjectToAsset("test", canvas.Consume());
canvas.Dispose();
What I've tried
- Using a
CommandBufferinstead of directly usingGraphics.Blit; - Copying the
RenderTextureto a plainTexture2Dto save in the asset; - Skipping the double-buffering helper and ensure I'm just calling
Blitonce in a single texture.