Uv displacement shader

Hi guys,

I’m quite new to Unity shaders and would like to ask for your help. I didn’t know where to post my question, either here, or in Image Effects subforum, hope posting here is correct.

The question is the following. For my project I’m planning to project virtual environment onto non-planar surface (cylinder) from three beamers. With the help of additional program I obtain the UV map of the display I’m going to project onto. One map calculated for three projectors. It looks like this:

Based on this UV texture I need to distort the original texture (I believe I should render to texture from main camera), and draw the outcome to main display (or to several displays separately).

As far as I understand, I should use Graphics.Blit function, however all my attempts so far are unsuccessful. And most of the tutorials are about animated UV displacement which is too complicated for my case.

Basically, I ask for any kind of guidance or, best case, example code of how to .Blit Render Texture and specific UV texture and output the final texture either directly on the display or to another texture.

Thank you in advance.

My ugly shader made with ShaderForge

Shader "Shader Forge/Displacement" {
Properties {
_MainTex ("MainTex", 2D) = "white" {}
_Flow ("Flow", 2D) = "white" {}
}
SubShader {
Tags {
"Queue"="Geometry+1"
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
ZTest Always
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#define UNITY_PASS_FORWARDBASE
#define _GLOSSYENV 1
#include "UnityCG.cginc"
#include "UnityPBSLighting.cginc"
#include "UnityStandardBRDF.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma only_renderers d3d9 d3d11 glcore gles
#pragma target 3.0
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform sampler2D _Flow; uniform float4 _Flow_ST;
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord0 : TEXCOORD0;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.uv0 = v.texcoord0;
o.normalDir = UnityObjectToWorldNormal(v.normal);
v.vertex.xyz = float3((o.uv0*float2(1.0,_ProjectionParams.r)),0.0);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.pos = UnityObjectToClipPos( v.vertex );
return o;
}
float4 frag(VertexOutput i) : COLOR {
i.normalDir = normalize(i.normalDir);
float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
float3 normalDirection = i.normalDir;
float3 viewReflectDirection = reflect( -viewDirection, normalDirection );
////// Lighting:
////// Emissive:
float4 _Flow_var = tex2D(_Flow,TRANSFORM_TEX(i.uv0, _Flow));
float3 node_3339 = (float3(i.uv0,0.0)+(((float3(_Flow_var.rgb.rg,0.0)*2.0)-1.0)*0.25));
float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(node_3339, _MainTex));
float3 emissive = _MainTex_var.rgb;
float3 finalColor = emissive;
return fixed4(finalColor,1);
}
ENDCG
}
}
CustomEditor "ShaderForgeMaterialInspector"
}

float4 texUV = tex2D(_UVTexture, i.uv);
return tex2D(_MainTex, texUV.xy) * texUV.z;

That’s really about it. If you’re doing anything more, I suspect something is wrong elsewhere.

Random thoughts:

  • The UV texture should be a linear space, uncompressed, 16 bit per channel (or better) texture. If it’s not you likely won’t have enough precision in the UVs to sample your texture nicely. Getting floating point textures into Unity without it doing things you don’t want can be a pain sometimes. You’ll need to be using linear space color for the project as otherwise Unity always applies an sRGB curve to the data.
  • The UV texture should not have any anti-aliasing. I’m assuming the image you posted is just a scaled down version of some larger texture.
  • I’m also assuming the blue channel is intended as a mask.
  • When using Blit, you’ll need the material to be pre setup with the UV texture assigned. The _MainTex should get assigned automatically if you set it as the first parameter of the Blit, but sometimes it doesn’t so try assigning it manually via script.
  • I don’t think Shader Forge shaders work with Blit very well. You may need to write this out by hand, or use Amplify Shader Editor which I think does have better image effect shader handling.
1 Like

Dear bgolus, thank you very much for your reply and your suggestions.
In principle, as far as I understand the shader works (even though it’s messy and made with ShaderForge), if, as a UVtexture, I assign some random distortion.
Original camera view (Equirectangular 360)

Distorted

However in my case, since my UVtexture has these black borders, which are necessary for projector to illuminate only the display, at the background I also get the view from the camera.

The cylinder on the UVtexture is horizontal because my projectors are installed vertically, I will rotate the texture later and adjust everything. But first, could you please help me, how can I assign some specific texture (rendertexture) as the MainTex, instead of what camera renders currently, and how to get rid of this “backgroud render” and make the display area, which is not covered by UV map remain black. In addition when I try to assign render texture from the main camera as MainTex, the camera to which the shader is applied still renders what it sees (or black screen if I put it somewhere from the scene and choose “Depth Only”. So basically it ignores the MainTex.
Hope you understand what I mean. This shader is the only thing left fro the whole project to be complete.
Thank you very much in advance.

Either when you call blit, use your render texture as the first input, or change the shader to not use “_MainTex”. You don’t actually need a _MainTex in the shader at all, just name it _NotMainTex and it’ll work, assuming you’re assigning the render texture on the material before hand (which it appears you are in the screenshot above).

If your UV texture is using a blue channel as a mask, ie: it’s black where it should be black and blue where it should be visible, the above code snippet should work. ie: multiply the texture’s color by the UV texture’s blue channel.

However I’m quite confused as to how you’re getting anything useful in the background. The UV texture doesn’t appear to have any UVs in the black areas, and the shader you posted above isn’t use a blend function, and even if it was is outputting an alpha of 1, so it should be opaque and showing single corner pixel color value across the entire background.

1 Like

Hi, bgolus, thank you very much for your tips!

Indeed writing a shader myself was much easier than trying to “ease” the process by using Shader Forge.
Everything works perfectly now! Thanks again.

1 Like