Offset parameters

The docs say:
Offset Factor, Units

But I can’t figure out what this means. I presume factor is a multiplier for the units, but in what direction? Toward/Away from the camera? Along the surface normal?

I’m trying to simulate a highlight effect by stacking mesh clones with different materials, but I’m having difficulty getting the highlight material to render on top. I tried to utilize the options given in the BlendedDecal shader from the wiki, but it doesn’t seem to make any difference how I set the offset values.

Here’s my current attempt in case my problem is obvious:

Shader “Vertex Highlight Constant” {
Properties {
_Color (“Main Color”, Color) = (1,1,1,1)
_MainTex (“Base (RGB)”, 2D) = “white” {}
}

SubShader {

ColorMaterial AmbientAndDiffuse
Lighting Off
SeperateSpecular Off
ZTest LEqual
ZWrite Off
Tags{“Queue” = “Transparent”}

Pass {
Offset -1, -1

SetTexture [_MainTex] {
Combine texture * primary, texture * primary
}
SetTexture [_MainTex] {
constantColor [_Color]
Combine previous * constant, previous * constant
}
}
}

Fallback " VertexLit", 1
}

I am also interested in an answer for this.

The offset is towards or away from the camera. The way the values are used is complicated, because it’s implementation-dependent. You can find a little more information in the OpenGL docs for glPolygonOffset(), but it’s probably down to trial and error in the end.

Thanks. From this statement…

…it would seem to me that the offset values ought to be (1, -1), for the usage found in this thread, but it’s (-1, -1) instead. Why is that? It seems to me that you should multiply the depth by 1, and then move closer by r. Why doesn’t multiplying by -1 place the polygons behind the camera?

(I don’t know much about OpenGL yet.)

If both coefficients are negative, the result is a negative offset, reducing depth (bringing the vertex closer to the camera).

I don’t understand that.

1 * DZ + r * -1

suggests to me that you take the Z-depth of what is onscreen, then move it closer by the smallest allowable amount. Obviously I’m not understanding what DZ is.

-1 * DZ

suggests to me that you take the distance from the camera of what is onscreen, and move it behind the camera by that distance.

The result of that equation is the offset, not the depth. The offset is added to the computed depth of the vertices. DZ is not just Z depth. As you quoted, “DZ is a measurement of the change in depth relative to the screen area of the polygon.” I don’t know exactly what this means, but it sounds like a derivative of depth with respect to screen position.

Here’s what we want from the OpenGL FAQ:

Here’s a pet peeve of mine:

Offset OffsetFactor , OffsetUnits
Set depth offset. Note that this command intentionally only accepts constants (i.e., not shader parameters) as of Unity 3.0.

intentionally

intentionally

why.jpg

Honeslty. Why? Why can’t you let us make our own decisions? Why do you assume we have no idea what we are doing and purposefully impose limitations to keep us safe from some nonexistent danger? ._.

2 Likes

I also am very unhappy about the inability to set the offset as a per material parameter right now. If anyone knows a workaround for this please reply.

2 Likes

Late follow up but it should save a headache to someone as its near first hint when researching usage of Offset.
Parameterize Offsets works for me in unity 2018 with extremely high units, such large as -+1000000 range. I am not sure this is intended.

1 Like

So, this is a pretty crazy thread necro, and there are probably other posts that explain why but I’ll explain it again here.

Offset is an graphics API feature, not something Unity created. This is important because of how it’s specified in the OpenGL spec. Or more importantly how it is not specified.
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml

That part I bolded, “implementation-specific”, means it’s up to whatever the GPU manufacturer decides it is. Which means every single GPU is a little different… Or sometimes a lot different. Also note the feature is intended to be used for pushing two things that are coplanar be slightly not coplanar. Which means two surfaces that are overlapping and flickering, this is supposed to let you make one be slightly closer to the camera so it’s in top. If you’re trying to use it to move something closer to the camera by a significant distance, anything more than “ever so slightly”, it’s probably not what you want to use.

3 Likes

Yeah on the topic of shifting depth alone. Until you noted depth change per unit is undefined between systems I was thinking it a simpler solution of depth layering passes (such as outline hulls) precisely without doing extra clip matrix math in vertex.

Doing a clip z offset in the vertex shader is cheaper than using Offset, as strange as that sounds. Offset is applied after the fragment is processed, which means the GPU can’t do any early fragment rejection, and means you pay the cost of the entire mesh being drawn even if it’s behind other objects. Adjusting the clip position in the vertex shader means the GPU can still do rejection of occluded fragments.

5 Likes

Would you mind explaining “clip z offset in vertex shader” a bit further?

The position the vertex shader outputs is the homogeneous clip space position. It’s 4 component position representing a -w to +w “on screen” range on the x and y, and either -w to +w or 0.0 to w on the z “in near / far plane” range, with the w being either the view space depth or 1.0 depending on if it’s a perspective or orthographic projection.

You can partially replicate an Offset using something like this:

// the code you usually see in a vertex shader to calculate the clip space position
o.pos = UnityObjectToClipPos(v.vertex)

// offset z with a very small value
// note this is positive unlike the Offset render state values
float offset = 0.00001;
#if defined(UNITY_REVERSED_Z)
// all APIs but OpenGL use a reversed depth, so add to bring closer to the camera
o.pos.z += offset * o.pos.w;
#else
// else if OpenGL you need to subtract to bring closer to the camera
o.pos.z -= offset * o.pos.w;
#endif

Adjust that offset value to whatever you need to get it to be visible.

The real Offset does quite a bit more than just that one offset towards the camera, but it’s implementation changes between different APIs, and even different devices, so there’s no “one” implementation. For Direct3D the spec for this is an offset “unit” of 1.0 (second value) will offset the depth by exactly the smallest floating point precision for the current depth value, where as the “factor” offsets based on a bit of a magic number depending on how much the surface is facing away from the camera, and even on Direct3D the implementation appears to be device specific.

1 Like