Hello!
I have been refactoring code that use custom cubemap stored as octohedron map in an atlas, it use to work, but now I have weird behavior I don’t if that’s normal or not.
Basically I hash the position of the mesh data to select the appropriate cubemap, simple in theory, and it use to work flawlessly.
Now in the refactoring, instead of using a prefab in the scene, I instance the mesh by code, position it, apply the material. The shader itself now render to a texture, which is then applied to the mesh.
But after troubleshooting, nothing make sense at all. It seems that the world position, which is code untouched, doesn’t behave properly and break everything down.
in the old code rendering position gave:
define as
struct d
{
float4 vertex : POSITION;
float2 uv : TEXCOORD1;
fixed4 color : COLOR;
fixed3 normal : NORMAL;
};
struct v2f
{
float4 vertex : POSITION;
float4 wpos : TEXCOORD1;
fixed4 color : COLOR;
fixed3 wnormals : NORMAL;
};
in both code
old vertex code do
v2f vert (d v)
{
v2f o;
//vertex world position
o.wpos = mul(unity_ObjectToWorld, v.vertex);
//vertex screen position
o.vertex = UnityObjectToClipPos(v.vertex);
//normal to world normal
o.wnormals =UnityObjectToWorldNormal(v.normal);
//o.vertex = UnWrapToScreenSpace(float2 v.uv, float4 v.vertex);
o.color = float4(v.uv, 0,1);// v.color;
return o;
}
New code vertex shader do
rasterData vertexProgram (meshData input)
{
rasterData output;
output.wpos = mul(unity_ObjectToWorld, input.vertex); //world position
// output.wpos = mul ( _PosMat, input.vertex );//world position
output.vertex = unwrap(input.uv, input.vertex.w); //screen position
//output.vertex = UnityObjectToClipPos(input.vertex); //screen position
output.wnormals =UnityObjectToWorldNormal(input.normal); //normal to world normal
output.color = float4(input.uv, 0,1);// v.color;
return output;
}
The main difference is that I ouput to rendertexture in the new code, as such vertex position is using UV space, debug show it work flawlessly.
Fragment is virtually unchanged, but it’s where everything seems to break:
fixed4 fragmentProcessing (rasterData input) : COLOR
{
//set size
const float size = 4;
const float2 cuberange = float2(16,16);
float epsilon = 0.000001;
float3 origin = _Origin.xyz;
float3 worldnorm = normalize(input.wnormals) + epsilon;
float3 pos = input.wpos.xyz - origin;// + 0.0001;
//hash position to read the right cubemap in the atlas
// float3 hashpos = floor(pos / size);
float3 hashpos = floor(input.wpos.xyz / size);
float3 hash_offset = hashpos * size;
float2 hash_id = max(float2(0,0), min(hashpos.xz, cuberange));
//box projection
float3 cubecenter = hash_offset + (size / 2);
float3 mincube = hash_offset + 0;
float3 maxcube = hash_offset + size;
float3 projected = BoxProjectVector(pos, worldnorm, cubecenter, mincube, maxcube);
//sampling the atlas
float2 octnormal = (PackNormalToOct(projected) + 1) / 2;
float2 samplepos = (octnormal + hash_id) / cuberange;
//cubemap result
float4 cubesample = tex2D ( _Atlas, samplepos );
cubesample = tex2D ( _Atlas, input.color.xy );
//light
float3 light = normalize(_MainLight);
float ndotl = saturate(dot(light.xyz, worldnorm));
// float skyocclusion = saturate(dot(float3(0,1,0), worldnorm));//should be wrap lighting
float skyocclusion = wrappedTo1(worldnorm,float3(0,1,0));
//skyocclusion *= skyocclusion;
//shadow sampling, box projected and direct
float3 lightproj = BoxProjectVector(pos, light, cubecenter, mincube, maxcube);
float2 lightbox = (PackNormalToOct(lightproj) + 1) / 2;
float2 shadowbox = (lightbox + hash_id) / cuberange;
float2 lightdirect = (PackNormalToOct(light) + 1) / 2;
float2 shadowdirect = (lightdirect + hash_id) / cuberange;
float4 boxshadow = tex2Dlod( _Atlas, float4(shadowbox,0,7));//tex2D(_MainTex, shadowtest);
float4 boxshadow2 = tex2Dlod( _Atlas, float4(shadowbox,0,0));//tex2D(_MainTex, shadowtest);
float4 directlight = tex2Dlod( _Atlas, float4(shadowdirect,0,4));
float4 occlufactor = tex2Dlod( _Atlas, float4(shadowdirect,0,7));
float4 occlusion = occlufactor.b * (skyocclusion + 1.0);
// return fixed4(1,0,0,1);
// return fixed4(skyocclusion,0,0,1);
// return fixed4(ndotl,0,0,1);
// return fixed4(lightproj,1);
// return fixed4(hashpos*64,1);
//return fixed4(frac(pos/4),1);
return float4(pos,1);
//return fixed4(lightproj,1);
//return fixed4(shadowdirect*64,0,1);
//return fixed4(lightdirect,0,1);
//return fixed4(lightbox,0,1);
//return fixed4(floor(pos*8.0)/32,1);
//return fixed4(origin*64,1);
//return fixed4(hash_offset,1);
//return fixed4(hash_id*64,0,1);
//return fixed4(light,1);
//return (input.color);
//float4 nt = 0.8;return frac(nt);
// return cubesample;
// return boxshadow2;
//return directlight;
//return occlufactor;
// return occlusion;
}
Outputing the pos no longer give me the same result
Which doesn’t make any sense.
Trying to output visual values also show that after floor() it return only zeroes (black texture even after boosting), the haspos variable being bogus and contaminating everything down.
If I had a single modification (like just a space character) while play in running, it reload the shader and show the expected data.
BUT it lose the binding to the atlas texture
Hypothesis: Maybe unity only initialize unity_ObjectToWorld just before rendering, the texture generation code is called within start.
void Start(){
// void Startup(){
foreach (var scene in scenes){
scene.root = Instantiate(DebugRoot,
new Vector3(32,0,32),
//Vector3.zero,
//Quaternion.Euler(0,0,0)
Quaternion.Euler(-90,0,0)
//Quaternion.identity
);
scene.init(setLight(), shaders);
}
}
// int s = 0;
void Update(){
// if (s != 2){Startup();s+=1;}
// Debug.Log(s);
foreach (var scene in scenes){
updateLight();
scene.updateLight();//TODO: when light change, refresh
scene.updateGI();
}
}
BUT if I rename Start into startup and delay calling it inside update, it never shift to the expected data.
Another idea was to bypass unity_ObjectToWorld and pass my own matrix by code:
//debug world matrix in shader
positionMatrix.SetTRS(
new Vector3(0.5f,.0f,0), //position
Quaternion.Euler(
0,
0,
0
), //rotation
new Vector3(4,4,4) //scale
);
directPass.SetMatrix("_PosMat", positionMatrix);
//end debug
(code above is the last test, original was passing basic matrix)
I get the same result with bogus wpos, and tweaking the matrix never give me the same data than the expected one …
I thought it was issue with : TEXCOORD1 instead of : POSITION, but original code was already passing texcoord and worked correctly.
I’m kinda at loss as to what’s happening, analyse of the code data flow show that problem arise before the rendering to texture and unrelated to the the unwrapping and the rendertexture.
But I’m a loss as to what’s happening.
This is urgent matter, help!