添加光时环境光变得更亮(OpenGL,法线贴图)
Ambient light is getting brighter when a light is added (OpenGL, Normal mapping)
最近几天我在 Java (Libgdx) 玩了闪电。我是 OpenGL 或着色器的新手,我偶然发现了一个很好的教程如何使用法线贴图实现照明 (https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6)。到目前为止,我设法用一盏灯做到了这一点,现在我正试图用多盏灯做同样的效果。我尝试使用加法混合为每盏灯执行一次绘制调用。阴影绘制正确,但每次我添加灯光时,环境颜色都会变亮。我尝试了一些方法,但没有任何效果,我卡住了。
我的渲染方法:
@Override
public void render () {
renderToFbo(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY());
renderToScreen(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY());
renderToFbo(200, 200);
batch.setBlendFunction(GL_ONE,GL_ONE_MINUS_SRC_COLOR);
renderToScreen(200,200);
renderToFbo(500, 500);
batch.setBlendFunction(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
renderToScreen(500,500);
}
private void renderToFbo(float posX, float posY){
fbo.begin();
batch.setBlendFunction(GL_ONE, GL_ZERO);
batch.setShader(defaultShader);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(lightMap, posX - lightSize / 2, posY - lightSize / 2, lightSize,lightSize);
batch.end();
fbo.end();
}
private void renderToScreen(float posX, float posY){
batch.setShader(lightningShader);
batch.begin();
float x = posX / (float) Gdx.graphics.getWidth();
float y = posY / (float) Gdx.graphics.getHeight();
LIGHT_POS.x = x;
LIGHT_POS.y = y;
lightningShader.setUniformf("lightPos", LIGHT_POS.x, LIGHT_POS.y, LIGHT_POS.z);
fbo.getColorBufferTexture().bind(2);
normalMap.bind(1);
texture.bind(0);
batch.draw(texture, 0,0);
batch.end();
}
这是我的片段着色器:
varying vec4 vColor;
varying vec2 vTexCoord;
uniform sampler2D u_texture; //diffuse map
uniform sampler2D u_normals; //normal map
uniform sampler2D u_light; //light map
uniform vec2 resolution; //resolution of screen
uniform vec3 lightPos; //light position, normalized
uniform vec4 lightColor; //light RGBA -- alpha is intensity
uniform vec4 ambientColor; //ambient RGBA -- alpha is intensity
void main() {
//RGBA of our diffuse color
vec4 diffuseColor = texture2D(u_texture, vTexCoord);
//RGB of our normal map
vec3 normalMap = texture2D(u_normals, vTexCoord).rgb;
//NormalMap.g = 1.0 - NormalMap.g;
//The delta position of light
vec3 lightDir = vec3(lightPos.xy - (gl_FragCoord.xy / resolution.xy), lightPos.z);
lightDir.x *= resolution.x / resolution.y;
//normalize our vectors
vec3 N = normalize(normalMap * 2.0 - 1.0);
vec3 L = normalize(lightDir);
//Pre-multiply light color with intensity
//Then perform "N dot L" to determine our diffuse term
vec3 diffuse = (lightColor.rgb * lightColor.a) * max(dot(N, L), 0.0);
//pre-multiply ambient color with intensity
vec3 ambient = ambientColor.rgb * ambientColor.a;
//calculate attenuation from lightmap
vec2 lighCoord = (gl_FragCoord.xy / resolution.xy);
vec3 attenuation = texture2D(u_light, lighCoord).rgb;
//the calculation which brings it all together
vec3 intensity = ambient + diffuse * attenuation;
vec3 finalColor = diffuseColor.rgb * intensity;
gl_FragColor = vColor * vec4(finalColor, diffuseColor.a);
}
要通过单个渲染调用执行此操作,您的片段着色器必须接受要处理的灯光位置数组。着色器必须在编译时知道数组大小,因此您应该将数组设置得足够大以容纳您需要的尽可能多的灯(当您需要的灯更少时,您可以将剩余的灯设置为黑色)。
我在下面调整了你的着色器,只是假设它在你的代码上面可以正常工作。我不知道你在用光照贴图做什么,所以我用更传统的东西代替了你的衰减计算。
varying vec4 vColor;
varying vec2 vTexCoord;
uniform sampler2D u_texture; //diffuse map
uniform sampler2D u_normals; //normal map
const int LIGHT_COUNT = 4;
uniform vec2 resolution; //resolution of screen
uniform vec3[LIGHT_COUNT] lightPos; //light position, normalized
uniform vec4[LIGHT_COUNT] lightColor; //light RGBA -- alpha is intensity
uniform vec4 ambientColor; //ambient RGBA -- alpha is intensity
void main() {
vec4 diffuseColor = texture2D(u_texture, vTexCoord);
vec3 normalMap = texture2D(u_normals, vTexCoord).rgb;
vec3 N = normalize(normalMap * 2.0 - 1.0);
float resolutionFactor = resolution.x / resolution.y;
vec3 diffuse = new vec3(0.0);
for (int i=0; i<LIGHT_COUNT; i++){
vec3 lightDir = vec3(lightPos[i].xy - (gl_FragCoord.xy / resolution.xy), lightPos[i].z);
lightDir.x *= resolutionFactor;
vec3 L = normalize(lightDir);
float distance = length(lightDir);
vec3 attenuation = 1.0 / ( 0.4 + 3.0*distance + (20.0*distance*distance ) );
diffuse += attenuation * (lightColor[i].rgb * lightColor[i].a) * max(dot(N, L), 0.0);
}
//pre-multiply ambient color with intensity
vec3 ambient = ambientColor.rgb * ambientColor.a;
//the calculation which brings it all together
vec3 intensity = min(vec3(1.0), ambient + diffuse); // don't remember if min is critical, but I think it might be to avoid shifting the hue when multiple lights add up to something very bright.
vec3 finalColor = diffuseColor.rgb * intensity;
gl_FragColor = vColor * vec4(finalColor, diffuseColor.a);
}
将光照参数传递给着色器:
static final int LIGHT_COUNT = 4;
final float[] tmpLightPositions = new float[3 * LIGHT_COUNT];
final float[] tmpLightColors = new float[4 * LIGHT_COUNT];
//...
int i = 0;
for (Vector3 pos : myLightPositions) {// should be LIGHT_COUNT of them
tmpLightPositions[i++] = pos.x;
tmpLightPositions[i++] = pos.y;
tmpLightPositions[i++] = pos.z;
}
i = 0;
for (Color col : myLightColors) {
tmpLightColors[i++] = color.r;
tmpLightColors[i++] = color.g;
tmpLightColors[i++] = color.b;
tmpLightColors[i++] = color.a;
}
shader.setUniform3fv("lightPos", tmpLightPositions, 0, tmpLightPositions.length);
shader.setUniform4fv("lightColor", tmpLightColors, 0, tmpLightColors.length);
最近几天我在 Java (Libgdx) 玩了闪电。我是 OpenGL 或着色器的新手,我偶然发现了一个很好的教程如何使用法线贴图实现照明 (https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6)。到目前为止,我设法用一盏灯做到了这一点,现在我正试图用多盏灯做同样的效果。我尝试使用加法混合为每盏灯执行一次绘制调用。阴影绘制正确,但每次我添加灯光时,环境颜色都会变亮。我尝试了一些方法,但没有任何效果,我卡住了。
我的渲染方法:
@Override
public void render () {
renderToFbo(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY());
renderToScreen(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY());
renderToFbo(200, 200);
batch.setBlendFunction(GL_ONE,GL_ONE_MINUS_SRC_COLOR);
renderToScreen(200,200);
renderToFbo(500, 500);
batch.setBlendFunction(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
renderToScreen(500,500);
}
private void renderToFbo(float posX, float posY){
fbo.begin();
batch.setBlendFunction(GL_ONE, GL_ZERO);
batch.setShader(defaultShader);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(lightMap, posX - lightSize / 2, posY - lightSize / 2, lightSize,lightSize);
batch.end();
fbo.end();
}
private void renderToScreen(float posX, float posY){
batch.setShader(lightningShader);
batch.begin();
float x = posX / (float) Gdx.graphics.getWidth();
float y = posY / (float) Gdx.graphics.getHeight();
LIGHT_POS.x = x;
LIGHT_POS.y = y;
lightningShader.setUniformf("lightPos", LIGHT_POS.x, LIGHT_POS.y, LIGHT_POS.z);
fbo.getColorBufferTexture().bind(2);
normalMap.bind(1);
texture.bind(0);
batch.draw(texture, 0,0);
batch.end();
}
这是我的片段着色器:
varying vec4 vColor;
varying vec2 vTexCoord;
uniform sampler2D u_texture; //diffuse map
uniform sampler2D u_normals; //normal map
uniform sampler2D u_light; //light map
uniform vec2 resolution; //resolution of screen
uniform vec3 lightPos; //light position, normalized
uniform vec4 lightColor; //light RGBA -- alpha is intensity
uniform vec4 ambientColor; //ambient RGBA -- alpha is intensity
void main() {
//RGBA of our diffuse color
vec4 diffuseColor = texture2D(u_texture, vTexCoord);
//RGB of our normal map
vec3 normalMap = texture2D(u_normals, vTexCoord).rgb;
//NormalMap.g = 1.0 - NormalMap.g;
//The delta position of light
vec3 lightDir = vec3(lightPos.xy - (gl_FragCoord.xy / resolution.xy), lightPos.z);
lightDir.x *= resolution.x / resolution.y;
//normalize our vectors
vec3 N = normalize(normalMap * 2.0 - 1.0);
vec3 L = normalize(lightDir);
//Pre-multiply light color with intensity
//Then perform "N dot L" to determine our diffuse term
vec3 diffuse = (lightColor.rgb * lightColor.a) * max(dot(N, L), 0.0);
//pre-multiply ambient color with intensity
vec3 ambient = ambientColor.rgb * ambientColor.a;
//calculate attenuation from lightmap
vec2 lighCoord = (gl_FragCoord.xy / resolution.xy);
vec3 attenuation = texture2D(u_light, lighCoord).rgb;
//the calculation which brings it all together
vec3 intensity = ambient + diffuse * attenuation;
vec3 finalColor = diffuseColor.rgb * intensity;
gl_FragColor = vColor * vec4(finalColor, diffuseColor.a);
}
要通过单个渲染调用执行此操作,您的片段着色器必须接受要处理的灯光位置数组。着色器必须在编译时知道数组大小,因此您应该将数组设置得足够大以容纳您需要的尽可能多的灯(当您需要的灯更少时,您可以将剩余的灯设置为黑色)。
我在下面调整了你的着色器,只是假设它在你的代码上面可以正常工作。我不知道你在用光照贴图做什么,所以我用更传统的东西代替了你的衰减计算。
varying vec4 vColor;
varying vec2 vTexCoord;
uniform sampler2D u_texture; //diffuse map
uniform sampler2D u_normals; //normal map
const int LIGHT_COUNT = 4;
uniform vec2 resolution; //resolution of screen
uniform vec3[LIGHT_COUNT] lightPos; //light position, normalized
uniform vec4[LIGHT_COUNT] lightColor; //light RGBA -- alpha is intensity
uniform vec4 ambientColor; //ambient RGBA -- alpha is intensity
void main() {
vec4 diffuseColor = texture2D(u_texture, vTexCoord);
vec3 normalMap = texture2D(u_normals, vTexCoord).rgb;
vec3 N = normalize(normalMap * 2.0 - 1.0);
float resolutionFactor = resolution.x / resolution.y;
vec3 diffuse = new vec3(0.0);
for (int i=0; i<LIGHT_COUNT; i++){
vec3 lightDir = vec3(lightPos[i].xy - (gl_FragCoord.xy / resolution.xy), lightPos[i].z);
lightDir.x *= resolutionFactor;
vec3 L = normalize(lightDir);
float distance = length(lightDir);
vec3 attenuation = 1.0 / ( 0.4 + 3.0*distance + (20.0*distance*distance ) );
diffuse += attenuation * (lightColor[i].rgb * lightColor[i].a) * max(dot(N, L), 0.0);
}
//pre-multiply ambient color with intensity
vec3 ambient = ambientColor.rgb * ambientColor.a;
//the calculation which brings it all together
vec3 intensity = min(vec3(1.0), ambient + diffuse); // don't remember if min is critical, but I think it might be to avoid shifting the hue when multiple lights add up to something very bright.
vec3 finalColor = diffuseColor.rgb * intensity;
gl_FragColor = vColor * vec4(finalColor, diffuseColor.a);
}
将光照参数传递给着色器:
static final int LIGHT_COUNT = 4;
final float[] tmpLightPositions = new float[3 * LIGHT_COUNT];
final float[] tmpLightColors = new float[4 * LIGHT_COUNT];
//...
int i = 0;
for (Vector3 pos : myLightPositions) {// should be LIGHT_COUNT of them
tmpLightPositions[i++] = pos.x;
tmpLightPositions[i++] = pos.y;
tmpLightPositions[i++] = pos.z;
}
i = 0;
for (Color col : myLightColors) {
tmpLightColors[i++] = color.r;
tmpLightColors[i++] = color.g;
tmpLightColors[i++] = color.b;
tmpLightColors[i++] = color.a;
}
shader.setUniform3fv("lightPos", tmpLightPositions, 0, tmpLightPositions.length);
shader.setUniform4fv("lightColor", tmpLightColors, 0, tmpLightColors.length);