我正在使用现代OpenGL来创建图形引擎。使用phong和blinn-phong着色模型进行眼睛空间照明计算(法线,镜面反射......)的一切都很好。但是当我尝试实现法线贴图(使用TBN空间计算)时,我的高光照明是错误的:
这是我的渲染代码(它是java,但我认为方法和类名对所有程序员都是可以理解的):
// This method renders the given scene to the default framebuffer
public static void renderScene(Scene sce){
Map<Model, List<Entity>> requests = new HashMap<Model, List<Entity>>();
// Gets all entities and bind them to a model for optimization
for(Entity e : sce.getEntities()){
if(requests.containsKey(e.getModel())){
requests.get(e.getModel()).add(e);
}else{
List<Entity> newBatch = new ArrayList<Entity>();
newBatch.add(e);
requests.put(e.getModel(), newBatch);
}
}
Vector4f clearColor = sce.getBackgroundColor();
// Enable some parameters...
glEnable(GL_MULTISAMPLE);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
// Clear the buffers
glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render
renderAll(requests, sce, materialProg);
// Clear entities and batch
requests.clear();
}
// This method render all the given entities (1 model per entity) using the given scene properties (camera, clear color, ambient color, lights)
private static void renderAll(Map<Model, List<Entity>> requests, Scene sce, Program prog){
Camera cam = sce.getCamera();
Matrix4f viewMatrix = cam.getViewTransform();
Matrix4f projMatrix = cam.getProjTransform();
// Bind the material program
prog.bind();
// Sets the projection and view matrix uniforms
prog.setUniformValue("projection", projMatrix);
prog.setUniformValue("view", viewMatrix);
// Sets other parameters
prog.setUniformValue("ambientColor", sce.getAmbientColor());
prog.setUniformValue("blinnPhong", true);
// This is the list of enabled lights only (disabled lights are removed from sce.getLights())
List<Light> lights = new ArrayList<Light>();
// Add all enabled lights
for(Light l : sce.getLights()){
if(l.isEnabled()){
lights.add(l);
}
}
// The number of light used (MAX_LIGHTS is a constant, I've set it to 10)
int numLights = Math.min(MAX_LIGHTS, lights.size());
prog.setUniformValue("numLights", numLights);
// The light matrix. Maybe the problem is here.
// It transform the light direction from world space to eye/cam space
Matrix3f lightMatrix = new Matrix3f(viewMatrix).invert().transpose();
for(int i = 0; i < numLights; i++){
Light l = lights.get(i);
prog.setUniformValue("allLights["+i+"].color", l.getColor());
prog.setUniformValue("allLights["+i+"].position", viewMatrix.transformPosition(l.getPosition(), new Vector3f()));
prog.setUniformValue("allLights["+i+"].direction", lightMatrix.transformDirection(l.getDirection(), new Vector3f()));
prog.setUniformValue("allLights["+i+"].power", l.getPower());
prog.setUniformValue("allLights["+i+"].attenuation", l.getAttenuation());
prog.setUniformValue("allLights["+i+"].coneAngle", l.getConeAngle());
prog.setUniformValue("allLights["+i+"].coneBlend", l.getConeBlend());
prog.setUniformValue("allLights["+i+"].isDirectional", l.isDirectional());
}
// Rendering all models
for(Model m : requests.keySet()){
// For each model to render
m.getVAO().bind();
if(m.isBackfaceCulling()){
glEnable(GL_CULL_FACE);
}else{
glDisable(GL_CULL_FACE);
}
glPolygonMode(GL_FRONT_AND_BACK, m.getPolygonMode());
// Material settings
prog.setUniformValue("diffuseColor", m.getMaterial().getDiffuseColor());
prog.setUniformValue("shininess", m.getMaterial().getShininess());
prog.setUniformValue("specularColor", m.getMaterial().getSpecularColor());
prog.setUniformValue("specularIntensity", m.getMaterial().getSpecularIntensity());
prog.setUniformValue("normalMapIntensity", m.getMaterial().getNormalMapIntensity());
prog.setUniformValue("textureRepeat", m.getMaterial().getTextureRepeat());
if(m.getMaterial().getDiffuseTexture() != null){
prog.setUniformValue("diffuseTex", 0, m.getMaterial().getDiffuseTexture());
prog.setUniformValue("hasDiffuseTex", true);
}else{
prog.setUniformValue("hasDiffuseTex", false);
}
if(m.getMaterial().getNormalTexture() != null){
prog.setUniformValue("normalTex", 1, m.getMaterial().getNormalTexture());
prog.setUniformValue("hasNormalTex", true);
}else{
prog.setUniformValue("hasNormalTex", false);
}
for(Entity e : requests.get(m)){
Matrix4f modelView = viewMatrix.mul(e.getTransform(), new Matrix4f());
Matrix3f normalMatrix = new Matrix3f(modelView).invert().transpose();
// For each entity that use this model
prog.setUniformValue("model", e.getTransform());
prog.setUniformValue("normalMatrix", normalMatrix);
glDrawElements(m.getDrawMode(), m.getVertexCount(), GL_UNSIGNED_INT, 0);
}
m.getVAO().unbind();
}
prog.unbind();
}
我的着色器
顶点着色器
#version 450 core
// max lights
#define MAX_LIGHTS 10
// current number of light used
uniform int numLights;
// array of lights
uniform struct Light {
vec3 color;
vec3 position;
vec3 direction;
float power;
float attenuation;
float coneAngle;
float coneBlend;
bool isDirectional;
} allLights[MAX_LIGHTS];
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat3 normalMatrix;
uniform float textureRepeat;
// Order is important:
// 1: positions
// 2: normals
// 3: uvs
// 4: tangents
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 uv;
layout(location = 3) in vec3 tangent;
out vec2 vUV;
out vec3 vToCamera;
out vec3 vPositionTBNSpace;
out vec3 vLightDirections[MAX_LIGHTS];
out vec3 vLightPositions[MAX_LIGHTS];
void main(){
// Normal in camera space
vec3 vNormalCamSpace = normalize(normalMatrix * normal);
// Tangent in and cam space
vec3 vTangentCamSpace = normalize(normalMatrix * tangent);
// Bitangent in cam space
vec3 vBitangentCamSpace = normalize(cross(vNormalCamSpace, vTangentCamSpace));
mat3 vTBN = transpose(mat3(vTangentCamSpace, vBitangentCamSpace, vNormalCamSpace));
// Position in camera space
vec3 positionCamSpace = (view * model * vec4(position, 1.0)).xyz;
vPositionTBNSpace = vTBN * positionCamSpace;
vToCamera = normalize(vTBN * (-positionCamSpace));
for(int i = 0; i < numLights; i++){
vLightDirections[i] = vTBN * allLights[i].direction;
vLightPositions[i] = vTBN * allLights[i].position;
}
// UV (texture coords)
vUV = uv * textureRepeat;
// Final position
gl_Position = projection * vec4(positionCamSpace, 1.0);
}
片段着色器
#version 450 core
// max lights
#define MAX_LIGHTS 10
// current number of light used
uniform int numLights;
// array of lights
uniform struct Light {
vec3 color;
vec3 position;
vec3 direction;
float power;
float attenuation;
float coneAngle;
float coneBlend;
bool isDirectional;
} allLights[MAX_LIGHTS];
// the ambient color
uniform vec3 ambientColor;
// the diffuse texture
uniform bool hasDiffuseTex;
uniform sampler2D diffuseTex;
// the normal texture
uniform bool hasNormalTex;
uniform sampler2D normalTex;
// material settings
uniform bool blinnPhong;
uniform vec3 diffuseColor;
uniform vec3 specularColor;
uniform float specularIntensity;
uniform float normalMapIntensity;
uniform float shininess;
in vec2 vUV;
in vec3 vToCamera;
in vec3 vPositionTBNSpace;
in vec3 vLightDirections[MAX_LIGHTS];
in vec3 vLightPositions[MAX_LIGHTS];
out vec4 color;
// Functions
// Apply a light
vec3 applyLight(Light light, vec3 lightDirection, vec3 lightPosition, vec3 surfaceColor, vec3 normal, vec3 surfacePos, vec3 surfaceToCamera) {
vec3 surfaceToLight;
float attenuation = 1.0;
if(light.isDirectional) {
// directional light
surfaceToLight = normalize(-lightDirection);
attenuation = 1.0; //no attenuation for directional lights
} else {
// point light
surfaceToLight = normalize(lightPosition - surfacePos);
float distanceToLight = length(lightPosition - surfacePos);
attenuation = 1.0 - pow(clamp(distanceToLight / light.attenuation, 0.0, 1.0), 2);
// cone restrictions (affects attenuation)
float lightToSurfaceAngle = degrees(acos(dot(-surfaceToLight, normalize(lightDirection))));
if(lightToSurfaceAngle < light.coneAngle){
// in the shape of the cone
if(lightToSurfaceAngle > light.coneBlend){
attenuation *= 1.0 - (lightToSurfaceAngle - light.coneBlend) / (light.coneAngle - light.coneBlend);
}
}else{
attenuation = 0.0;
}
}
// diffuse
float diffuseCoefficient = clamp(dot(normal, surfaceToLight), 0.0, 1.0);
vec3 diffuse = diffuseCoefficient * surfaceColor * light.color;
// specular
float specularCoefficient = 0;
if(blinnPhong){
specularCoefficient = pow(clamp(dot(normal, normalize(surfaceToLight + surfaceToCamera)), 0.0, 1.0), shininess) * diffuseCoefficient;
}else{
specularCoefficient = pow(clamp(dot(surfaceToCamera, reflect(-surfaceToLight, normal)), 0.0, 1.0), shininess) * diffuseCoefficient;
}
vec3 specular = specularCoefficient * specularIntensity * specularColor * light.color;
// color
return attenuation * (diffuse + specular) * light.power;
}
// Main
void main(){
// Lighting and texturing
vec3 finalColor = vec3(0);
vec4 textureColor = vec4(diffuseColor, 1.0);
vec3 normalTBNSpace = vec3(0.0, 0.0, 1.0);
if(hasDiffuseTex){
textureColor *= texture(diffuseTex, vUV);
}
if(hasNormalTex){
normalTBNSpace = mix(vec3(0.0, 0.0, 1.0), normalize(texture(normalTex, vUV).rgb * 2.0 - 1.0), normalMapIntensity);
}
vec3 surfaceToCamera = normalize(vToCamera);
for(int i = 0; i < numLights; i++){
finalColor += applyLight(allLights[i], vLightDirections[i], vLightPositions[i], textureColor.rgb, normalTBNSpace, vPositionTBNSpace, surfaceToCamera);
}
color = vec4((ambientColor * textureColor.rgb) + finalColor, textureColor.a);
// color = (vec4(vToCamera.xyz, 1.0) + 1) / 2;
}
感谢您的帮助。