Smooth shader in OpenGL for OBJ import? - c++

I'm using the OpenGL OBJ loader, which can be downloaded here!
I exported an OBJ model from Blender.
The problem is that I want to achieve a smooth shading.
As you can see, it is not smooth here.
How do I achieve this? Maybe something is wrong at the normal vector calculator?
float* Model_OBJ::calculateNormal( float *coord1, float *coord2, float *coord3 )
{
/* calculate Vector1 and Vector2 */
float va[3], vb[3], vr[3], val;
va[0] = coord1[0] - coord2[0];
va[1] = coord1[1] - coord2[1];
va[2] = coord1[2] - coord2[2];
vb[0] = coord1[0] - coord3[0];
vb[1] = coord1[1] - coord3[1];
vb[2] = coord1[2] - coord3[2];
/* cross product */
vr[0] = va[1] * vb[2] - vb[1] * va[2];
vr[1] = vb[0] * va[2] - va[0] * vb[2];
vr[2] = va[0] * vb[1] - vb[0] * va[1];
/* normalization factor */
val = sqrt( vr[0]*vr[0] + vr[1]*vr[1] + vr[2]*vr[2] );
float norm[3];
norm[0] = vr[0]/val;
norm[1] = vr[1]/val;
norm[2] = vr[2]/val;
return norm;
}
And glShadeModel( GL_SMOOTH ) is set.
Any ideas?

If you want to do smooth shading, you can't simply calculate the normal for each triangle vertex on a per-triangle basis as you're doing now. That would yield flat shading.
To do smooth shading, you want to sum up the normals you calculate for each triangle to the associated vertices and then normalize the result for each vertex. That will yield a kind of average vector which points in a smooth direction.
Basically take what you're doing and add the resulting triangle normal to all of its vertices. Then normalize the summed vectors for each vertex. It'd look something like this (pseudocode):
for each vertex, vert:
vert.normal = vec3(0, 0, 0)
for each triangle, tri:
tri_normal = calculate_normal(tri)
for each vertex in tri, vert:
vert.normal += tri_normal
for each vertex, vert:
normalize(vert.normal)
However, this should normally not be necessary when loading from an OBJ file like this, and a hard surface model like this typically needs its share of creases and sharp corners to look right where the normals are not completely smooth and continuous everywhere. OBJ files typically store the normals for each vertex inside as the artist intended the model to look. So I'd have a look at your OBJ loader and see how to properly fetch the normals contained inside the file out of it.

Thanks pal! I found the option in Blender to export normal vectors, so now I have completely rewritten the data structure to manage normal vectors too. Now it looks smooth! image

Related

Problem implementing reflections in ray tracing

I've implemented a simple ray tracer and now I'm trying to implement reflections but objects are behaving as transparent.
Here is my code for getting the reflected ray.
ray* reflected = new ray();
reflected->direction = rayIn.direction - (2 * glm::dot(rayIn.direction, normal)) * normal;
reflected->origin = int_point + epsilon * normal;
outColor += ((int_object->reflectivity)*intersectray(*reflected, depth - 1));
Here are images With code:
Without code:
I'll edit the post if more code is needed.
Edit : It seems the problem is when I'm iterating through the objects in the scene. I insert the objects as
scene->add(sphere1);
scene->add(sphere2);
But when I change this to :
scene->add(sphere2);
scene->add(sphere1);
the output is correct.
Sphere 1 is at closer to camera than sphere 2 and they are not overlapping.
Problem was this part of code
for (objects in scene){
double intersection = (*objIterator)->intersect(rayIn,normal);
if (intersection < minDistance && intersection > epsilon )
{
minDistance = intersection;
int_object = *objIterator;
int_point = rayIn.origin + intersection * rayIn.direction + (epsilon * normal);
}}
Here normal is used later for other calculations but the first line update normal for current object intersection (Even if its not close). So I added a vector to store normal of the intersection object and used it later.

OpenGL Normal Mapping Issues - Normals Possibly Facing Wrong Direction?

I am currently working on my first OpenGL based game engine. I need normal mapping as a feature, but it isn't working correctly.
Here is an animation of what is Happening
The artifacts are affected by the angle between the light and the normals on the surface. Camera movement does not affect it in any way. I am also (at least for now) going the route of the less efficient method where the normal extracted from the normal map is converted into view space rather than converting everything to tangent space.
Here are the relevant pieces of my code:
Generating Tangents and Bitangents
for(int k=0;k<(int)mb->getIndexCount();k+=3)
{
unsigned int i1 = mb->getIndex(k);
unsigned int i2 = mb->getIndex(k+1);
unsigned int i3 = mb->getIndex(k+2);
JGE_v3f v0 = mb->getVertexPosition(i1);
JGE_v3f v1 = mb->getVertexPosition(i2);
JGE_v3f v2 = mb->getVertexPosition(i3);
JGE_v2f uv0 = mb->getVertexUV(i1);
JGE_v2f uv1 = mb->getVertexUV(i2);
JGE_v2f uv2 = mb->getVertexUV(i3);
JGE_v3f deltaPos1 = v1-v0;
JGE_v3f deltaPos2 = v2-v0;
JGE_v2f deltaUV1 = uv1-uv0;
JGE_v2f deltaUV2 = uv2-uv0;
float ur = deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x;
if(ur != 0)
{
float r = 1.0 / ur;
JGE_v3f tangent;
JGE_v3f bitangent;
tangent = ((deltaPos1 * deltaUV2.y) - (deltaPos2 * deltaUV1.y)) * r;
tangent.normalize();
bitangent = ((deltaPos1 * -deltaUV2.x) + (deltaPos2 * deltaUV1.x)) * r;
bitangent.normalize();
tans[i1] += tangent;
tans[i2] += tangent;
tans[i3] += tangent;
btans[i1] += bitangent;
btans[i2] += bitangent;
btans[i3] += bitangent;
}
}
Calculating the TBN matrix in the Vertex Shader
(mNormal corrects the normal for non-uniform scales)
vec3 T = normalize((mVW * vec4(tangent, 0.0)).xyz);
tnormal = normalize((mNormal * n).xyz);
vec3 B = normalize((mVW * vec4(bitangent, 0.0)).xyz);
tmTBN = transpose(mat3(
T.x, B.x, tnormal.x,
T.y, B.y, tnormal.y,
T.z, B.z, tnormal.z));
Finally here is where I use the sampled normal from the normal map and attempt to convert it to view space in the Fragment Shader
fnormal = normalize(nmapcolor.xyz * 2.0 - 1.0);
fnormal = normalize(tmTBN * fnormal);
"nmapcolor" is the sampled color from the normal map.
"fnormal" is then used like normal in the lighting calculations.
I have been trying to solve this for so long and have absolutely no idea how to get this working. Any help would be greatly appreciated.
EDIT - I slightly modified the code to work in world space and outputted the results. The big platform does not have normal mapping (and it works correctly) while the smaller platform does.
I added in what direction the normals are facing. They should both be generally the same color, but they're clearly different. Seems the mTBN matrix isn't transforming the tangent space normal into world (and normally view) space properly.
Well... I solved the problem. Turns out my normal mapping implementation was perfect. The problem actually was in my texture class. This is, of course, my first time writing an OpenGL rendering engine, and I did not realize that the unlock() function in my texture class saved ALL my textures as GL_SRGB_ALPHA including normal maps. Only diffuse map textures should be GL_SRGB_ALPHA. Temporarily forcing all textures to load as GL_RGBA fixed the problem.
Can't believe I had this problem for 11 months, only to find it was something so small.

Assimp calculating bones

Ive been trying to load my skinned collada mesh using assimp for the last few days but am having an extremely hard time. Currently I am just trying to render it in its normal "pose" without any transformations or animations but the mesh comes very deformed. I think the problem lies in my bones, not my weights so I am only posting the bone section.
Here is my bone loading code:
// this is done for every bone in a for loop:
Bone new_bone;
new_bone.name = std::string(bone->mName.data);
aiMatrix4x4 b_matrix = bone->mOffsetMatrix;
aiMatrix4x4 g_inv = scene->mRootNode->mTransformation;
g_inv.Inverse(); // according to a tutorial, you have to multiply by inverse root
b_matrix = b_matrix * g_inv;
memcpy(new_bone.matrix, &b_matrix, sizeof(float) * 16);
Bones.push_back(new_bone);
Then I simply send this to my shader with
glUniformMatrix4fv(MatrixArray, bone_count, GL_FALSE, &MATRIX_BUFFER[0][0][0]);
And apply it in the vertex shader with:
mat4 Bone = V_MatrixArray[int(Indicies[0])] * Weights[0];
Bone += V_MatrixArray[int(Indicies[1])] * Weights[1];
Bone += V_MatrixArray[int(Indicies[2])] * Weights[2];
Bone += V_MatrixArray[int(Indicies[3])] * Weights[3];
vec4 v = Bone * vec4(Vertex, 1);
gl_Position = MVP * vec4(v.xyz, 1);
This code mostly works, however my mesh is very deformed.... It looks like this:
According to the reserach ive done so far:
I do not need to transpose my matrices since assimp uses OpenGL column major
I do not need to read the nodes of the scene yet since they are for
animation
Please correct me if I am mistaken about these last 2 things.
I managed to solve it. Turns out you DO need to read the nodes, even for a simple bind pose without animation. For any future readers with this issue, each bones matrix = root_node_inverse * matrix concatenated from node heirechy * bone offset matrix.

GLSL: calculating normals after tesselation

I am having problems calculating normals after tesselation.
Currently I have code which samples height map and calculates normal from that:
float HEIGHT = 2048.0f;
float WIDTH =2048.0f;
float SCALE =displace_ratio;
vec2 uv = tex_coord_FS_in.xy;
vec2 du = vec2(1/WIDTH, 0);
vec2 dv= vec2(0, 1/HEIGHT);
float dhdu = SCALE/(2/WIDTH) * (texture(height_tex, uv+du).r - texture(height_tex, uv-du).r);
float dhdv = SCALE/(2/HEIGHT) * (texture(height_tex, uv+dv).r - texture(height_tex, uv-dv).r);
N = normalize(N+T*dhdu+B*dhdv);
But doesn't look ok with low level tesselations
How can I get rid of this ?
Only way to get rid of this is to use a normal map in combination with the computed normals. The normals you see on the right are correct. They're just in low resolution, because you tesselate them so. Use a normal map and per-pixel lighting to highlight the intricate details.
Also, one thing to consider is the topology of your initial mesh. More evenly spaced polygons result in more evenly spaced tesselation.
Additionally, you might want to do, instead of:
float dhdu = SCALE/(2/WIDTH) * (texture(height_tex, uv+du).r - texture(height_tex, uv-du).r);
float dhdv = SCALE/(2/HEIGHT) * (texture(height_tex, uv+dv).r - texture(height_tex, uv-dv).r);
sample a few more points from the heightmap, and average them to extract a more averaged version of the normal at each point.

How to achieve smooth tangent space normals?

I'm trying to add bump mapping functionality to my application but I'm getting very faceted models:
The reason it is happening is because I'm calculating tangent, binormal and normal on per face basis and completely ignoring the normals I'm getting from the model file.
The calculation currently uses two edges of the triangle and texture space vectors to get tangent and binormal, which are then used to calculate normal by cross product. It is all done on the CPU as soon as the model loads and the values are then stored as a part of model's geometry.
vector1 = vertex2.coords - vertex1.coords;
vector2 = vertex3.coords - vertex1.coords;
tuVector = vertex2.texcoords - vertex1.texcoords;
tvVector = vertex3.texcoords - vertex1.texcoords;
float den = 1.0f / (tuVector.x * tvVector.y - tuVector.y * tvVector.x);
tangent.x = (tvVector.y * vector1.x - tvVector.x * vector2.x) * den;
tangent.y = (tvVector.y * vector1.y - tvVector.x * vector2.y) * den;
tangent.z = (tvVector.y * vector1.z - tvVector.x * vector2.z) * den;
binormal.x = (tuVector.x * vector2.x - tuVector.y * vector1.x) * den;
binormal.y = (tuVector.x * vector2.y - tuVector.y * vector1.y) * den;
binormal.z = (tuVector.x * vector2.z - tuVector.y * vector1.z) * den;
D3DXVec3Normalize(&tangent, &tangent);
D3DXVec3Normalize(&binormal, &binormal);
D3DXVec3Cross(&normal, &tangent, &binormal);
D3DXVec3Normalize(&normal, &normal);
Is there a way to either calculate these values on per vertex basis, perhaps using the normal supplied with the model or to smooth them out somehow so the model doesn't appear faceted?
For smooth surfaces (no edges) I do it like this:
create space for per vertex
double N[3]; //normal
int cnt;
per vertex init
N={0.0,0.0,0.0}
cnt=0;
compute per face normal
normal must be normalized length=1.0 !!! add this Normal to all vertexes used in face and increment cnt to all vertexes used in face
per vertex normalize
N/=cnt; // N = average normal from all vertex - neighbour faces
be aware of cnt=0 for unused vertexes (division by zero)
per vertex N contains the Normal you want
now compute T,B vectors for TBN matrix (per vertex) as you do now
output image is smooth
My earth preview (with atmospheric scattering, bump mapping and more...) is here
hope it helps