Planimeter / game-framework

Planimeter Game Framework - LuaJIT FFI-based game development framework for Lua
MIT License
89 stars 3 forks source link

Bitangents Assimp and gltf #6

Closed revolucas closed 6 years ago

revolucas commented 6 years ago

I believe to get correct looking normals you need the mBitangent accessor from the aimesh and calculate the bitangentW in the shader as:

vec3 bitangentW = normalize(vec3(model * vec4(bitangent.xyz, 0.0)));

If you load up NormalTangentMirrorTest.gltf, you'll see the U and V mirror examples are messed up.

https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/NormalTangentMirrorTest

andrewmcwatters commented 6 years ago

@revolucas Thanks for the heads up! I'm sure there's a few different permutations of available mesh data and default shaders that the framework doesn't yet support, and I didn't stumble upon this during my testing.

I'll check this out today.

andrewmcwatters commented 6 years ago

@revolucas Didn't get the chance to check this out yet. I hold open game nights with friends on Thursdays. You wanna chat this one over this weekend and maybe I can push out a fix?

revolucas commented 6 years ago

It's alright, mate, just making you aware of it. I don't personally need you to bust out of fix. If you need more detail, I can explain more thoroughly.

andrewmcwatters commented 6 years ago

@revolucas Checking this out. I wonder why bitangent calculations are ending up incorrect, when the reference shader produces what should be the equivalent of them.

andrewmcwatters commented 6 years ago

@revolucas screen shot 2018-04-21 at 8 23 42 am

screen shot 2018-04-21 at 8 23 21 am

Are these wrong? Maybe I'm missing something... Is it the reflections in the half-spheres not all rendering upright due to incorrect bitangents?

Edit: https://forum.facepunch.com/f/nerds/phns/What-are-you-working-on-v67-March-2017/111/#postcvdxpv

revolucas commented 6 years ago

They are supposed to be identical, as seen here: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/NormalTangentMirrorTest. They are not flipped or rotated like in your example. Here is what it looks like in my fork, using bitangents read from assimp: https://imgur.com/a/Hca3NH4

aimesh.mTangent is an aiVector3D, the 'w' component is lost. The shader as is expects a vec4 for the tangent but it is passed a vec3. The value for 'w' will be 0 when calculating the bitangent. It needs to be either -1 or 1. An example is that a symmetric mesh will usually have one side be a mirror of the other, such as a human, that is why handedness is necessary. Assimp, already calculates the bitangent using the 'w' component. So all that needs done is to get mBitangent from the aimesh and extend the attributes in the mesh code and shader code to support bitangent, then add a new in varible for the shader called bitangent. Then replace how bitangentW is calculated with:

vec3 bitangentW = normalize(vec3(model * vec4(bitangent.xyz, 0.0)));

As a side note, I'm not sure what causes the strange artifacting and lines. Might have something to do with the images being jpeg and how DevIL is processing them.

Example snippets of what changes need done:

local stride = (3+3+3+3+2)

vertices[stride*i+9] = mesh.mBitangents[i].x
vertices[stride*i+10] = mesh.mBitangents[i].y
vertices[stride*i+11] = mesh.mBitangents[i].z 
    local position = GL.glGetAttribLocation(shader,"position")
    local normal = GL.glGetAttribLocation(shader,"normal")
    local tangent = GL.glGetAttribLocation(shader,"tangent")
    local bitangent = GL.glGetAttribLocation(shader,"bitangent")
    local texcoord = GL.glGetAttribLocation(shader,"texcoord")

    GL.glEnableVertexAttribArray(position)
    GL.glEnableVertexAttribArray(normal)
    GL.glEnableVertexAttribArray(tangent)
    GL.glEnableVertexAttribArray(bitangent)
    GL.glEnableVertexAttribArray(texcoord)

    local stride = (3+3+3+3+2)*ffi.sizeof("GLfloat")
    local vno = ffi.cast("GLvoid *",3*ffi.sizeof("GLfloat"))
    local vta = ffi.cast("GLvoid *",(3+3)*ffi.sizeof("GLfloat"))
    local vbi = ffi.cast("GLvoid *",(3+3+3)*ffi.sizeof("GLfloat"))
    local vte = ffi.cast("GLvoid *",(3+3+3+3)*ffi.sizeof("GLfloat"))

    GL.glVertexAttribPointer(position,3,GL.GL_FLOAT,0,stride,nil)
    GL.glVertexAttribPointer(normal,3,GL.GL_FLOAT,0,stride,vno)
    GL.glVertexAttribPointer(tangent,3,GL.GL_FLOAT,0,stride,vta)
    GL.glVertexAttribPointer(bitangent,3,GL.GL_FLOAT,0,stride,vbi)
    GL.glVertexAttribPointer(texcoord,2,GL.GL_FLOAT,0,stride,vte)
#define HAS_TANGENTS
#ifdef HAS_TANGENTS
in vec3 tangent;
in vec3 bitangent;

vec3 bitangentW = normalize(vec3(model * vec4(bitangent.xyz, 0.0)));//cross(normalW, tangentW) * tangent.w;

andrewmcwatters commented 6 years ago

Thanks for your help, @revolucas!