Hello guys, I hope somebody can help me on this, as I am not sure if I am doing things right. For a beginner in custom rendering the Render Graph documentation is pretty confusing/lackluster, even with the included samples.
What I want to achieve
I want to implement the approach in this video (https://youtu.be/jguyR4yJb1M?si=nu5e5rQIBhq1jLq5&t=98) to achieve pixel perfect rendering with smooth camera movement.The steps are as follows:
- Render scene at desired resolution with an extra 1 pixel border.
- Crop the 1 pixel border to achieve your desired resolution.
- Now shift your image within that extra 1 pixel border according to the camera’s offset from the pixe grid
- Output image.
My approach:
Currently stuck at cropping the image correctly.
Desired final resolution = 480 x 270 (16:9)
Set game resolution to 480 x 270. Use Unity pixel perfect camera script to render image at 482 x 272. In a custom render graph Blit the camera’s output to a 480x270 texture with a shader that does the cropping. Output the resulting image to the screen.
All my scripts and my shader are attached at the bottom.
The problem
The final output has artifacts in it. As you can see in the image below, there are vertical lines at certain spots where the original image isn’t sampled correctly. When I render the image at a square resolution (1:1) these artifacts do not appear. Therefore, I suspect that they are a result of floating-point imprecisions when transforming the UV’s in the shader. As far as I can see my shader calculations are correct, which leads me to believe that I cannot fix this problem in a shader.
The only solution to this that I can think of is to manually crop the image on the CPU in a double for loop, so that I can sample the image at discrete indices, but that would of course have a pretty big impact on performance.
Files
Render Feature and Pass
` using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.RenderGraphModule.Util;public class SmoothPixelPerfectRendererFeature : ScriptableRendererFeature
{
class SmoothPixelPerfectPass : ScriptableRenderPass
{
private Material material;
public SmoothPixelPerfectPass()
{
requiresIntermediateTexture = true;
}
public void Setup( Material material)
{
this.material = material;
}
// RecordRenderGraph is where the RenderGraph handle can be accessed, through which render passes can be added to the graph.
// FrameData is a context container through which URP resources can be accessed and managed.
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
var resourceData = frameData.Get<UniversalResourceData>();
var source = resourceData.activeColorTexture;
// The destination texture is created here,
var destinationDesc = renderGraph.GetTextureDesc(source);
destinationDesc.name = $"CameraColor-SmoothPixelPerfectPass";
destinationDesc.clearBuffer = false;
destinationDesc.width -= 2;
destinationDesc.height -= 2;
destinationDesc.filterMode = FilterMode.Point;
var destination = renderGraph.CreateTexture(destinationDesc);
// Blit
RenderGraphUtils.BlitMaterialParameters para = new(source, destination, material, 0);
renderGraph.AddBlitPass(para, passName: "SmoothPixelPerfectPass");
// Output edited image
resourceData.cameraColor = destination;
}
}
[SerializeField] private Material material;
SmoothPixelPerfectPass m_ScriptablePass;
/// <inheritdoc/>
public override void Create()
{
m_ScriptablePass = new SmoothPixelPerfectPass();
// Configures where the render pass should be injected.
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
}
// 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)
{
#if UNITY_EDITOR
if (renderingData.cameraData.isSceneViewCamera) return;
#endif
m_ScriptablePass.Setup(material);
renderer.EnqueuePass(m_ScriptablePass);
}
}