I have a problem with a shader I must write. I’m trying to get a screen projected texture scale with the object distance to the camera. It’s used with a Fresnel to mask the silhouette of objects. But the scale of the silhouette details stay the same as I move the camera around. I also can’t achor the texture to the screen position of the objects.

I tried a few things already, after some research:

The usual answer to this is the thing you already threw out, which is moving the noise to match the object’s screen space position. Otherwise what you’re doing should (mostly) work, or at least do something assuming the objects aren’t being batched.

If you’re really wanting to do something like screen space, but have it scale by distance, I’d recommend using the view space position for the UVs instead of using screen space at all.

// vertex shader
o.viewPos = UnityObjectToViewPos(v.vertex);
// fragment shader
fixed4 col = tex2D(_MainTex, i.viewPos.xy);

I’ll test that, i fixed the distance with a × instead of a / which is corrected above, i also added a .5 float2 that loosely maked anchored, i fugured i have a kind of barrel like distortion because distance to camera is a sphere while distance to screen is a plane, maybe your code will allow to make that happen.

Concerning batching, i think for anchoring it should works because share origin mean equal offset.

But for depth i thought about hashing the depth per pixel to have a simili multiplan. Let see how it panned out.

The thing I’m a bit confuse about is trying to debug and visualize the data, origin and world don’t return sny color, no matter how much offset and scale i apply, trying to get the distance with abs minus behave the same… why is then the distance functions return something functional?

One problem is unity_ObjectToWorld[3] isn’t the origin. That’s going to always be a bunch of zeros. You want unity_ObjectToWorld._m03_m13_m23, or use mul(unity_ObjectToWorld, float4(0,0,0,1)). unity_ObjectToWorld[3] is unity_ObjectToWorld._m30_m31_m32_m33 which for a non-projection matrix should always be equal to float4(0,0,0,1).

Assuming everything is working, the “distance” you want for scaling screen space UVs isn’t actually the real distance, it’s the view plane depth. Which, again, you can get from the view position. Then then you’d want to multiply that by a value in the projection matrix to account for the FOV.

Something like this:

float3 origin = unity_ObjectToWorld._m03_m13_m23;
// view space z, negative because view space is -z forward
float depth = -mul(UNITY_MATRIX_V, float4(origin, 1.0)).z;
float2 uv = i.spos.xy / i.spos.w;
// make 0,0 at the center of the screen
uv -= 0.5;
// correct for aspect ratio, if that's a thing you want
uv.x *= _ScreenParams.x / _ScreenParams.y;
// scale by depth and the fov
uv *= depth / UNITY_MATRIX_P._m11;

It’s working except the anchoring of the texture to the objects:

basically I want to offset the object texture by the amount of the object position in screen space, such the center of the texture is the center of mass of the object.

I need the position of the object in view space

offset the texture by the amount of the position

It’s non trivial for any objects where the local origin isn’t in the center of mass of the object, but i’m using a default unity sphere by now to simplify thing.

I don’t entirely understand what you’re trying to do there with that code.

Some general thoughts.

unity_ObjectToWorld._m03_m13_m23 is the world space pivot. Most of those functions are called Unity_Object_To for a reason, and in object space the pivot position is float3(0,0,0).

You do not need to (and really, shouldn’t) calculate the pivot position in the vertex shader to pass to the fragment shader. Either you should be modifying the the values being used for the UVs in the vertex shader using that position, or calculating it in the fragment shader. Otherwise it’s wasteful to calculate it in the vertex shader and pass it along, and probably causing yourself more pain than needed.

However you’re calculating the screen space UVs is also how you need to calculate the pivot position. You can’t mix and match view space, clip space, or “screen pos” and expect useful results. You also should be subtracting the pivot position, not adding it. And should be doing that before doing any other modifications to the UV you might be doing.

“Screen pos” is the same thing as clip space, just with the x and y values scaled and offset so that after the perspective divide (divide by w) it’s in a 0.0 to 1.0 range instead of the -1.0 to +1.0 range of clip space. Passing anything but a clip space position to it will produce junk.

My recommendation for you would be to just do something like this:

There’s no reason to complicate what you’re doing with actual screen position values, or aspect ratio / fov correction. Naively using the view space position gets you all of that.

I basically want to have the flatness of screen pos / w with the object space stability of the last code (which doesn’t have the same / w) but can’t figure out how that works…

perfect!
just an emulation of volume for fur and hair by having silouhette align to view and cropped by alpha, with noisy texture work fine as a stylization, I need to test on complex non convex mesh.

Well I made a varient where I replaced the hard earn and valuable object anchored screenspace with an attempt at tangent space interior shading, in order to gain angular stability…

I use the boxprojection from arm and octohedral mapping to dodge the environement and hum …

I mean I expect some deformation because I’m not using a map that do env correctly, but this is not stable when I move around …

I was wondering, can you help me figure out where there is a mistake? I mean it should be way more stable than what I got

//vertex
float3x3 objectToTangent (float3 normal, float4 tangent){
return float3x3(
tangent.xyz,
cross(normal, tangent.xyz) * tangent.w,
normal
);
}
float3 tangentViewDir (float3 normal, float4 tangent, float4 vertex){
return normalize(mul(objectToTangent(normal, tangent),ObjSpaceViewDir(vertex)));
}
o.viewTangent = tangentViewDir (v.normal, v.tangent, v.vertex);
//fragment
float3 boxproject(float2 uv, float3 direction){
//_EnviCubeMapPos – the cubemap origin position
const float3 center = 0.5;
//_BBoxMax – the bounding volume (bounding box) of the environment
const float3 bmax = 1;
//_BBoxMin – the bounding volume (bounding box) of the environment
const float3 bmin = 0;
//V – the vertex/fragment position in world space
float3 pos = float3(uv,0);
//L – the normalized vertex-to-light vector in world space
//Working in World Coordinate System.
//vec3 intersectMaxPointPlanes = (_BBoxMax - V) / L;
//vec3 intersectMinPointPlanes = (_BBoxMin - V) / L;
float3 invdir = 1 / direction;
float3 imax = ( bmax - pos ) * invdir; // / direction;
float3 imin = ( bmin - pos ) * invdir; // / direction;
// Looking only for intersections in the forward direction of the ray.
//vec3 largestRayParams = max(intersectMaxPointPlanes, intersectMinPointPlanes);
float3 m = max(imax,imin);
// Smallest value of the ray parameters gives us the intersection.
//float dist = min(min(largestRayParams.x, largestRayParams.y), largestRayParams.z);
float dist = min(min(m.x,m.y),m.z);
// Find the position of the intersection point.
//vec3 intersectPositionWS = V + L * dist;
float3 ipos = pos + direction * dist;
// Get the local corrected vector.
//Lp = intersectPositionWS - _EnviCubeMapPos;
return normalize(ipos - center);
}
float2 VectortoOctahedron( float3 N )
{
N /= dot( 1.0, abs( N ) ); // Equivalent to：N/= abs(N.x)+abs(N.y)+abs(N.z)
if( N.z <= 0 )
{
N.xy = ( 1 - abs( N.yx ) ) * ( N.xy >= 0 ? 1.0 : -1.0 );
}
return N.xy;
}
//box project
float freq = 5;
float3 vect = boxproject(frac(i.uv*freq), normalize(i.viewTangent));
float2 buv = VectortoOctahedron( vect );
col = tex2D(_MainTex, buv);

Many thanks @bgolus – you’re awesome and a wonderful gift!

Was facing one of those what-the-hell-am-i-doing-with-my-life moments trying to figure out SS .w math all over again, but then I found your posts with code and clarification.

So THANK YOU sir for being a saint in these forums and adding a TON of value for as long as these archives can be searched.