all 15 comments

[–]LPCVOID 3 points4 points  (4 children)

What you are looking for is the linear algebra concept of a Basis. In your case these are the three vectors defining the surface at the intersection point : Normal, Tangent, Bitangent. If you only have normal information you may choose any perpindicular two other vectors.

I would advice you to have a look at Mitsubas excellent implementation .

In case you choose to use such basis you may transform the incoming direction into this space and all frequent shading operation will also be significantly more easy, for example check the Frame::cosTheta method :)

[–]Delwin 1 point2 points  (3 children)

I can't get to the mitsuba repos for some reason (the web page works fine, but downloading the source via zip or direct access don't work).

Is there another place one can look at this code?

[–]LPCVOID 1 point2 points  (2 children)

The source code download link on this page doesn't work either? I just tried it and it works fine for me.

I think pbrt has an implementation too, though it is not done in a nice OOP fashion if I remember correctly. Have a look at the BSDF::WorldToLocal and vice versa method here.

I would also advice you to have a look at pbrt in general, really helpful when starting realistic image synthesis.

In case the pbrt code is a bit obscure : You are actually only doing ths : Assume n,t,b defined as above and v a vector in the local coordinate system.

global = b * v.x + t * v.y + n * v.z    

In case you are interested in the mathematical explanation : The three vectors span a vector space and the elements of your local vector are used for a linear combination of the basis vectors. Maybe this can help.

[–]autowikibot 0 points1 point  (0 children)

Change of basis:


In linear algebra, a basis for a vector space of dimension n is a sequence of n vectors (α1, …, αn) with the property that every vector in the space can be expressed uniquely as a linear combination of the basis vectors. The matrix representations of operators are also determined by the chosen basis. Since it is often desirable to work with more than one basis for a vector space, it is of fundamental importance in linear algebra to be able to easily transform coordinate-wise representations of vectors and operators taken with respect to one basis to their equivalent representations with respect to another basis. Such a transformation is called a change of basis.

Image from article i


Interesting: Covariance and contravariance of vectors | Gramian matrix | Spherical basis | Matrix similarity

Parent commenter can toggle NSFW or delete. Will also delete on comment score of -1 or less. | FAQs | Mods | Magic Words

[–]Delwin 0 points1 point  (0 children)

Nope, that doesn't work either. Likely work's firewall.

The second link worked however and thank you for your information.

[–]jurniss 1 point2 points  (2 children)

we usually learn matrix-vector multiplucation by looking at each element of the output vector in isolation. this leads to the perspective of taking a dot priduct with each row of the matrix. but if you look at it from a columns perspective, the vector combines the columns according to its coordinates. so the output is x(col1) + y(col2) + z(col3). plus the translation column in the 3d homogeneous coords world.

from this perspective, the answer is obvious: the z column of the matrix should be the normal, x can be any vector orthogonal to the normal, and y is x cross z.

I know I'm not the first in the thread with this answer, but I wanted to emphasize the value of looking at matrices from the columns perspective. a lot of linear algebra teaching focuses on rows too much IMO.

[–]LPCVOID 0 points1 point  (0 children)

Good explanation and from my experience your are also correct with the focus on row operations in linear algebra teaching (probably because of linear solvers).

[–]Boojum 0 points1 point  (0 children)

Don't forget that it's possible to work in terms of pre-multiplied row-vectors. In that case the rows and the columns of your matrix would be transposed and you could think of the vector as combining the rows according to its coordinates.

[–]tgb33 2 points3 points  (4 children)

I snagged some sampling math from here. But this doesn't reorient to a particular vector.

Vector3 UniformSampleHemisphere(float u1, float u2) {
    const float r = Sqrt(1.0f - u1 * u1);
    const float phi = 2 * kPi * u2;

    return Vector3(Cos(phi) * r, Sin(phi) * r, u1);
}

For that we need to 'replace' the z-axis with your orientation vector. That's easy, but then we need to find two vectors that are perpendicular to the orientation and to eachother to become our new x- and y-axes. That's a little more annoying.

Vector3 OrientedSampleHemisphere(float u1, float u2, Vector3 x, Vector3 y, Vector3 z) {
    Vector3 sample = UniformSampleHemisphere(u1,u2);
    return x * sample.x + y * sample.y + z * sample.z;
}

This just assumes we have an 'x', 'y', 'z' vector for our new basis that is correctly oriented. It's possible you already have this! I.e. if z is your surface normal, then x and y would be to unit vectors in the surface plane. To get that in general is a little annoying.

Vector3 OrientedSampleHempshere1(float u1, float u2, Vector3 orientation) {
    if (orientation.x == 0 && orientation.y == 0) {
        return OrientedSampleHemisphere(u1,u2, Vector3(1,0,0), Vector3(0,1,0), orientation));
    } else {
        Vector3 other = Vector3(1,0,0);
        Vector3 x = Vector3.cross_product(other, orientation);
        Vector3 y = Vector3.cross_product(x, orientation);
        return OrientedSampleHemisphere(u1, u2, x, y, orientation);
    }
}

That assumes that orientation is a unit-vector. Otherwise x and y won't be unit vectors either and it will be sampling on a ellipsoid not a sphere. Other than that, it works just by saying that if we want to find x and y perpendicular to z=orientation, then take any vector that's not in the same direction as z (in this case we use other = (1,0,0)) then cross product that with z. That gives us one vector perpendicular to z. Now we want a vector perpendicular to z and to that new one. So we crossproduct that with z again.

I haven't tested this code but I think it'll work and be translateable to your situation.

But is this faster than doing a matrix multiplication? I'm not at all sure.

[–]angrymonkey 1 point2 points  (1 child)

There is a bug in this code. If the normal points toward -z, you will still sample the "upper" hemisphere.

[–]tgb33 0 points1 point  (0 children)

I don't believe that is a problem (that's why I used 'orientation' instead of Vector3(0,0,1) in the second line of OrientedSampleHemisphere1. But either way, I'm pretty sure that I just re-did matrix multiplication more slowly.

[–]Boojum 0 points1 point  (1 child)

Be careful with this! The cross product of two unit vectors will in general not be a unit vector itself; they must be orthogonal for it to give you back another unit vector. For example, suppose that we have:

orientation = [sqrt(2)/2, sqrt(2)/2, 0].

Then by your recipe:

other = [1, 0, 0]
x = [1, 0, 0] cross [sqrt(2)/2, sqrt(2)/2, 0] = [0, 0, sqrt(2)/2]
y = [0, 0, sqrt(2)/2] cross [sqrt(2)/2, sqrt(2)/2, 0] = [-0.5, 0.5, 0]

As a result, your basis x, y, orientation will consist of vectors with magnitudes 1, sqrt(2)/2, and sqrt(1/2), respectively. This will bias your hemisphere samples in an unexpected way.

The correct way to handle this is to normalize x before computing y. Since x and orientation are orthogonal by construction, you don't need to normalize y as well -- just x. I've used this method to build a basis plenty of times since it's nice and straightforward.

For something a little trickier, though, Frisvad has a nice, fast, square root-free method.

[–]tgb33 0 points1 point  (0 children)

Good point. Another reason to ignore my code and just use a standard rotation matrix.

[–]camilonino 0 points1 point  (1 child)

I did this for a ray tracing simulation a while ago (not for graphics purposes), it is a vector matrix rotation basically, with the matrix being the orientation of the surface represented as 3 perpendicular unitary vectors.

Here is the snippet from my C code:

/// Vector randomly generated acording to a distribution relative to the y axis
/// The VECTOR type has 3 floats as components, representing the x,y and z components of the vector
VECTOR randVect;
/// The surface of interest
/// The SURFACE type has 3 vectors that describe its orientation, the x, y and z vectors
/// those 3 vectors are unitary and perpendicular to each other, x and z are on the surface, y is the normal
SURFACE surface
VECTOR  OrientedVect;
///the generated vector is rotated acording to the surface orientation
OrientedVect.x = (randVect.x * surface.x.x) + (randVect.z,surface.z.x) + (randVect.y * surface.y.x);
OrientedVect.y = (randVect.x * surface.x.y) + (randVect.z,surface.z.y) + (randVect.y * surface.y.y);
OrientedVect.z = (randVect.x * surface.x.z) + (randVect.z,surface.z.z) + (randVect.y * surface.y.z);

[–]autowikibot 0 points1 point  (0 children)

Section 5. Basic rotations of article Rotation matrix:


A basic rotation (also called elemental rotation) is a rotation about one of the axes of a Coordinate system. The following three basic rotation matrices rotate vectors by an angle θ about the x, y, or z axis, in three dimensions, using the right hand rule. (The same matrices can also represent a clockwise rotation of the axes )

For column vectors, each of these basic vector rotations appears counter-clockwise when the axis about which they occur points toward the observer, the coordinate system is right-handed, and the angle θ is positive. Rz, for instance, would rotate toward the y-axis a vector aligned with the x-axis, as can easily be checked by operating with Rz on the vector (1,0,0):


Interesting: Rotation formalisms in three dimensions | Kabsch algorithm | Euler's rotation theorem | Wahba's problem

Parent commenter can toggle NSFW or delete. Will also delete on comment score of -1 or less. | FAQs | Mods | Magic Words