Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411423 Posts in 69363 Topics- by 58416 Members - Latest Member: JamesAGreen

April 19, 2024, 02:18:12 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Relief Function shader
Pages: 1 [2]
Print
Author Topic: Relief Function shader  (Read 2717 times)
leotgo
Level 0
*


View Profile WWW
« Reply #20 on: September 11, 2018, 07:13:34 AM »

Hello.

I can't help with the maths, but hopefully these series of articles on Relief Mapping might be useful: http://www.inf.ufrgs.br/~oliveira/RTM.html

An example of the results:


EDIT: Nevermind, I absolutely skipped the part where you wanted to avoid the raymarching algorithms. Embarrassed
« Last Edit: September 11, 2018, 07:20:25 AM by leotgo » Logged

Leonardo Tagliaro
Porto Alegre - Brazil

Unity3D Programmer, looking forward to meet you!

Please feel free to email me anytime at [email protected]
gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #21 on: September 11, 2018, 03:17:53 PM »

@leotgo
My renaming got you, i'm sorry bro

anyway:

Quote
I took some more time to let myself think this one through. The problem comes down to when you have multiple "rows", you're scaling the UV space, which is effectively modifying the transform space the ray intersection is working in. You're then passing a ray that's normalized in unscaled tangent space to trace against planes that are in scaled tangent space. So you need to scale the ray before you normalize.

The reason why your parallax offset style modifications to the view dir were giving you something better looking is for two reasons. One, that magic number you're adding (be it pi or 0.42) helps to minimize the offset error. This is why it exists for parallax offset to begin with, it kind of "mushes" things. In this case it's helping to hide the errors caused by having a ray normalized in the wrong space. Additionally the mushing was probably getting it closer to the properly scaled ray direction.

Here's my version, which additionally handles non 1:1 aspect texture scaling


Code:
Shader "Unlit/Raytraced Rows"
{
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _Rows ("Number of Rows", Float) = 1
        _HalfDepth ("Half Depth", Float) = 0.2
    }
 
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Rows;
            float _HalfDepth;
 
            struct v2f {
                float4 pos : SV_Position;
                float2 uv : TEXCOORD0;
                float3 objectViewDir : TEXCOORD1;
                float3x3 objectToTangent : TEXCOORD2;
            };
 
            v2f vert (appdata_full v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
 
                float3 normal = normalize(v.normal.xyz);
                float3 tangent = normalize(v.tangent.xyz);
                float3 bitangent = normalize(cross(normal, tangent) * v.tangent.w);
                o.objectToTangent = float3x3(tangent, bitangent, normal);
 
                o.objectViewDir = ObjSpaceViewDir(v.vertex);
 
                return o;
            }
 
            float rayPlaneIntersection( float3 rayDir, float3 rayPos, float3 planeNormal, float3 planePos)
            {
                float denom = dot(planeNormal, rayDir);
                denom = max(denom, 0.000001); // avoid divide by zero
                float3 diff = planePos - rayPos;
                return -dot(diff, planeNormal) / denom;
            }
 
            fixed4 frag (v2f i) : SV_Target
            {
                // calculate tangent space view direction
                float3 tangentViewDir = mul(i.objectToTangent, i.objectViewDir);
 
                // build ray direction scaled to fit into a "row" space
                float3 rayDir = tangentViewDir;
                rayDir.x *= _Rows; // scale x based on number of rows
                rayDir.y *= _MainTex_ST.y / _MainTex_ST.x; // correct for texture scale aspect
                rayDir.z /= _HalfDepth; // scale z by inverse half depth
                rayDir = normalize(rayDir);
 
                // get row space uv
                float2 scaledUV = i.uv * float2(_Rows, 1);
                float2 fracUV = frac(scaledUV);
 
                // plane normals
                float cosine = 0.70710678; // 45 degrees
                float3 p0Normal = float3(cosine, 0, cosine);
                float3 p1Normal = float3(-cosine, 0, cosine);
 
                // get ray plane intersection distances
                float pt0 = rayPlaneIntersection(
                    rayDir, float3(fracUV.xy, 0), // ray
                    p0Normal, float3(0,0,0) // plane, starting at left edge of row space
                );
                float pt1 = rayPlaneIntersection(
                    rayDir, float3(fracUV.xy, 0), // ray
                    p1Normal, float3(1,0,0) // plane, starting at right edge of row space
                );
 
                // calculate and apply uv offset
                float ptMin = min(pt0, pt1);
                // scale ray back to unscaled tangent space and apply min range
                float2 uvOffset = rayDir.xy * float2(1 / _Rows, 1) * ptMin;
                float2 uv = i.uv - uvOffset;
 
                // use derivates from the original uv to avoid sampling artifacts on the peaks
                // -- not actually correct, but way easier than calculating the proper derivates --
                float2 uv_ddx = ddx(i.uv);
                float2 uv_ddy = ddy(i.uv);
                fixed4 col = tex2Dgrad(_MainTex, uv, uv_ddx, uv_ddy);
                return col;
            }
 
            ENDCG
        }
    }
}

Note, because the two planes are at 45 degrees, they "meet" halfway between the two sides of the row space. Hence "Half Depth". If they were both ~63 degrees they would meet at the center at a depth of "1" in row space.

Oh, one last thing. I do the object to tangent transform on the view direction in the fragment shader because the camera's tangent space position won't interpolate properly on anything that's not a flat plane with flat UVs. For the case of the planes used so far, it would have worked fine to transform the view direction there. But don't normalize that vector in the vertex shader or it won't interpolate properly again!


https://forum.unity.com/threads/relief-function-shader.549472/#post-3665737
Logged

gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #22 on: October 03, 2018, 04:58:19 PM »

I'm struggling to find the correct way to make ray intersection with curve that are swept along an axis.
In theory I thought it should be simple, I decompose the problem into component:
- find intersection on the curve (cross section), which is easy
- extrude that intersection point into an axis aligned line, solve the slope intersection to that line to get the final offset.

To be sure I got it right, I'm starting with a swept 45° line centered on origin (line-plane intersection with dot product would be more efficient, but remember I'm trying to validating swiping a long a line).
- line equation is origine + directionVector * t
- line to line intersection equation is t = (origine1 - origine2)/(directionVector2 - directionVector1) >> assuming they are never parallel.

So let line2dIntersection(directionVector1,origine1,directionVector2,origine2)

Assuming the ray start on the xy plane (pseudo code):
- I first compute the cross section into xz
intersection1 = line2dIntersection(rayDir.xz, vector2(origine.x,0), vector2(1,1).normalize(), vector2(0,0));
result.xz = raydir.xz * intersection1;

-Then find the slope swipe offset into yz
intersection2 = line2dIntersection(rayDir.yz, vector2(origine.y,0), vector2(1,0).normalize(), vector2(0,result.z));
result.y = raydir.y * intersection2;

But all my result are garbage. What am I doing wrong? where is the leap of logic I did?
Logged

gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #23 on: October 06, 2018, 06:11:51 PM »

I think my line equation is bogus, I assume t is the same in both equation, but I think I need a t1 and a t2
Logged

BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #24 on: October 08, 2018, 02:20:41 PM »

I'm not sure what you mean by vector division in your line to line intersection, but it seems wrong. You can google the correct formula. Also, it shouldn't need two line intersections - one is enough.

You haven't really made it clear what direction you are sweeping. I assme you have a cuve in xy plane, and sweeping in direction v. I'll use z for the vector pointing in the z axis. Then let's assume a ray defined by o + d * t (o the origin, d the direction).

Then we want to solve:

o + d * t1 = c + t2 * v

for some numbers t1, t2, and c a point on the curve.

Dotting both sides with z gives:

o.z + t1 * d.z = 0 + t2 * v.z

So t2 = (o.z + t1 * d.z) / v.z

Substituting back into the equation to solve and re-organizing, we get:

o + d * t1 = c + (o.z + t1 * d.z) / v.z * v

(o - o.z / v.z * v) + t1 * (d - d.z / v.z * v) = c

A + t1 * B = c

Oh look, that's the formula for a ray-curve intersection. And you'll find that both A and B are in the xy plane. Great, we can solve that according to whatever curve was selected. In your case you'd do a line-line intersection, but it works for any curve you can intersect in 2d. Once you have t1, then the actual point of intersection os o + d * t1.

Logged
gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #25 on: October 09, 2018, 02:58:06 PM »

Quote
I'm not sure what you mean by vector division in your line to line intersection
Sorry I was thinking in shader code, so it's component base math.
If I declare a float2 origine, it mean it's actually origine.xy (float4 would be origine.xyzw)
- so dividing by a float "a" give you origine.x/a and origine.y/a
- but dividing by float2 position (origine/position) would be origine.x/position.x and origine.y/position.y
- dividing only work for scalar (float) or same sized component (float2 can't divide float3 or float4, but a half2 can be)

Translating for easy reading:
Quote
o + d * t1 = c + t2 * v
Origine + direction * t = curvePoint + t2 * sweep

Quote
o.z + t1 * d.z = 0 + t2 * v.z So t2 = (o.z + t1 * d.z) / v.z
dot(Origine, zAxis) + t1 * dot(direction, zAxis) = 0 + t2 * dot(sweep, zAxis)
t2 = ( dot(Origine,zAxis) + t1 * dot(direction,zAxis) ) / dot(sweep, zAxis)

Quote
o + d * t1 = c + (o.z + t1 * d.z) / v.z * v

(o - o.z / v.z * v) + t1 * (d - d.z / v.z * v) = c

A + t1 * B = c
Origine + direction * t1 = curvepoint + ( dot(Origine,Zaxis) + t1 * dot(direction,Zaxis) ) / dot(sweep, zAxis) * sweep
A = (Origine - dot(Origine,zAxis)/ dot(sweep, zAxis) * sweep)
B = (direction - dot(direction, zAxis) / dot(sweep,zAxis) * sweep)
A + t1 * B = c

I'm not sure how I plug a curve in there, let's say I have a simple curve (2x-1)²
I have the ray starting on surface XZ such as raypos (x,0,z) and have a direction normal dn(nx,ny,nz)
I'm assuming the component nx and ny can characterize the projection of normal in the xy plane, but I need to normalized it back, so I would need to use nx1, and ny1 as the normalize projection to get to dnXY(nx1,ny1).
raypos2d(x,0) + dn(nx1,ny1) * t1 is the projected xy ray equation?

Then how do I intersect that with a non parametric form of (2x-1)²?
I guess that Curvepoint( x, (2x+1)² )

then there is an intersection when raypos2d(x,0) + dn(nx1,ny1) * t1 = Curvepoint( x, (2x-1)² ) ?

x + nx1 * t1 = x
t1 = 1/nx1?


0 + ny1 * t1 = (2x-1)²
t1 = (2x-1)² / ny1?

I'm bad at math, I feel like there is a huge chunk I don't know
« Last Edit: October 09, 2018, 03:07:36 PM by gimymblert » Logged

BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #26 on: October 11, 2018, 11:12:55 AM »

I've I've done is shown you how a raycast against a swept curve boils down to doing a different (2d) raycast vs the unswept curve. I didn't discuss how to do that raycast, as it is different for each curve.

In code terms, if you have a function:

Vec2 raycastToCurve(Vec2 origin, Vec2 direction)

which finds the intersection point for a given ray on your curve, then you can write a function like so

Vec3 raycastToSweptCurve(Vec3 origin, Vec3 direction, Vec3 sweep) {
Vec2 A,B = ...;// Computed as we discussed
// Solve 2d raycast
Vec2 p = raycastToCurve(A, B);
// Work out t1 (some raycast functions give you this value directly)
float t1 = b.x != 0 ? (p.x-A.x)/B.x : (p.y-A.y)/B.y;
// Move that distance along the original ray
return origin + t1 * direction;
}

Logged
gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #27 on: October 12, 2018, 10:13:47 AM »

Quote
I've I've done is shown you how a raycast against a swept curve boils down to doing a different (2d) raycast vs the unswept curve. I didn't discuss how to do that raycast, as it is different for each curve.

Yeah but that's the part I figure out, then I posted that I got my ray to ray equation wrong due to having a single t instead of one per "ray", which mean I couldn't derive the equation you ended up with. Though I'm not sure why you do a dot, there is probably different way to figure it out without dot? I hadn't done it yet due to burning out math, I can't process after a certain point.

The other problem was I was trying to resolve ray equation on curve with a different mathematical representation, but I scrapped that and decided to stay with simple spline and bezier (any parametric 2d curves), as they share parametric equation and therefore I can resolve them easily.

I'll try to solve the sweep along bezier curve (instead of ray) myself to see.

I had more here, linking back for easy personal navigation:
https://forum.unity.com/threads/relief-function-shader.549472/#post-3753844
Logged

gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #28 on: October 12, 2018, 10:18:22 AM »

Parametric surface (quadratic beizer) for fun and giggle:
- input: (1-t)²a +2t(1-t)b +t²c -o -vt
- solution: t = ( (2a-2b+v) +- sqr( (2b-2a-v)² -4(a-o)(a-2b+c) ) ) / (2(a-2b+c)

with a b and c the control point of the bezier
o the origine point of the ray, v the director, and t the interpolation percent.


EDIT:
I think I made the same t mistake again Huh?
Logged

BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #29 on: October 13, 2018, 02:47:58 AM »

As I said before, just google the formula for ray-to-ray intersection. You'll get a better write up than I can give here.
Logged
gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #30 on: October 13, 2018, 04:15:58 PM »

It's fine, I'm just trying to make sure I understand everything, it's not interesting to just copy paste formula if you don't understand them.
Logged

gimymblert
Level 10
*****


The archivest master, leader of all documents


View Profile
« Reply #31 on: October 18, 2018, 06:46:48 AM »

I found out that intersection can be expressed as the form of f(ray(t)), not sure though
So that (2x-1)² turn into (2(o+td)-1)²

t = ( (4d-8do)+-sqrt( (8do-4d)² - 16d²(4o²-4o+1) ) / 8d²

for d!=0




I did it the old way for quadratic bezier and used wolframAlpha

a(1-t1)² + b(1-t1)t1 + ct1² -0 +dt2

t1 = ( (2a-b) +- sqrt( (b-2a)² - 4(a-b+c) (a+dt2-o) ) / 2(a-b+c)

t2 = ( -a(1-t1)² + bt1² + bt1 - ct1² + o ) / d
Logged

Pages: 1 [2]
Print
Jump to:  

Theme orange-lt created by panic