4
$\begingroup$

I'm trying to achieve the same effect in OpenGL/GLSL as what Blender does when you add a texture, set to "Reflection"

This ultimately is a way to fake specularity, or "metallicness" if I get it right.

I followed this tutorial (Mid-page, "Sphere Mapping" section): http://www.ozone3d.net/tutorials/glsl_texturing_p04.php

Here's the problem(some serious artifacts): https://www.youtube.com/watch?v=hx66_xWhVu4&feature=youtu.be

Everything's happening at the bottom of the Vertex Sahder:

#version 330 core

layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
layout(location = 2) in vec3 vertexNormal_modelspace;
layout(location = 3) in vec3 vertexTangent_modelspace;

out vec2 uv;
out vec3 toLightVector[4];
out vec3 toCameraVector;
out float fog_Visibility;

uniform mat4 FTM;
uniform mat4 P;
uniform mat4 V;
uniform vec3 LP[4];

const float fog_Density = 0.2;
const float fog_Gradient = 0.9;

void main(){

    vec4 worldPosition = FTM * vec4(vertexPosition_modelspace, 1.0);
    uv = vertexUV;

    vec3 surfaceNormal = (FTM * vec4(vertexNormal_modelspace, 0.0)).xyz;

    vec3 norm = normalize(surfaceNormal);
    vec3 tang = normalize((FTM * vec4(vertexTangent_modelspace, 0.0)).xyz);
    vec3 bitang = normalize(cross(norm, tang));

    mat3 toTangentSpace = mat3(tang.x, bitang.x, norm.x, tang.y, bitang.y, norm.y, tang.z, bitang.z, norm.z);

    for (int i = 0; i < 4; i++) {
        toLightVector[i] = toTangentSpace * (LP[i] - worldPosition.xyz);
    }

    toCameraVector = toTangentSpace * ((inverse(V) * vec4(0.0, 0.0, 0.0, 1.0)).xyz - worldPosition.xyz);

    vec4 positionRelativeToCam = V * worldPosition;
    gl_Position = P * positionRelativeToCam;

    // Fog
    float fog_Distance = length(positionRelativeToCam.xyz);
    fog_Visibility = exp(-pow((fog_Distance * fog_Density), fog_Gradient));
    fog_Visibility = clamp(fog_Visibility, 0.0, 1.0);

    // Reflection Map
    vec3 u = normalize(toCameraVector);
    vec3 r = reflect(u, norm);
    float m = 2.0 * sqrt(r.x*r.x + r.y*r.y + (r.z + 1.0)*(r.z + 1.0));

    gl_TexCoord[0].s = r.x / m + 0.5;
    gl_TexCoord[0].t = r.y / m + 0.5;
}

And in the Fragment Shader, all I do is:

out_color = texture(reflectionSampler, gl_TexCoord[0].st);

The reflection map seems to do it's thing similarly to the way it's seen in Blender, but not quite.

I need to be able to do this without calculating lights. Thats how Blender does it, it works Shadeless mode so all it should take is the camera's vector...

$\endgroup$
3
$\begingroup$

You probably need to calculate the reflection vector per pixel, not per vertex. The calculation is non-linear, so doing it per vertex and interpolating will not give good results unless the model is finely tesselated.

Send norm and toCameraVector down to the pixel shader, normalize them both and move all your calculations under // Reflection Map to the pixel shader. That should give you some better results.

$\endgroup$
  • $\begingroup$ If the normals are varying between vertices, why should the tesselation be so important? $\endgroup$ – Jackalope May 11 '17 at 4:36
  • $\begingroup$ @Jackalope Tessellating more finely means the normals will be evaluated correctly at each tessellated vertex, and interpolated over a shorter distance between vertices, so the error due to using linear interpolation will be reduced. $\endgroup$ – Nathan Reed May 11 '17 at 4:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.