You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让OpenGL中多盏2D点光源重叠时显示效果自然?

多盏点光源重叠效果修复方案

问题根源

你当前的实现是每个光源单独渲染一次场景,并通过min()函数限制最终颜色不超过原纹理颜色。这种处理会导致多光源重叠时,后渲染的光源会把前一个光源提亮的颜色重新压回原纹理色,无法实现光照的累加效果,自然重叠区域显示异常。

正确实现流程(多光源累加)

采用帧缓冲(FBO)分层渲染的方式,将场景渲染和光照计算分离,最后合成结果:

  1. 先把整个场景渲染到一个帧缓冲,得到不带光照的基础场景纹理。
  2. 创建一个用于累积光照的帧缓冲,初始填充环境光颜色。
  3. 对每个点光源,计算其光照贡献并通过加法混合累加到光照缓冲中。
  4. 最后将场景纹理和光照纹理相乘,得到带多光源效果的最终画面。

对应着色器代码修改

1. 场景渲染着色器(生成无光照的基础场景)

顶点着色器

#version 400 core
in vec2 position;
in vec2 texCoords;
out vec2 passTexCoords;
uniform mat4 uProjection;
uniform mat4 uView;

void main() {
    gl_Position = uProjection * uView * vec4(position, 1.0, 1.0);
    passTexCoords = texCoords;
}

片段着色器

#version 400 core
in vec2 passTexCoords;
uniform sampler2D textureSampler;
uniform vec3 fontColor;

void main() {
    vec4 tex_color = texture(textureSampler, passTexCoords);
    if (tex_color.a < 1.0) discard;
    gl_FragColor = vec4(tex_color.rgb * fontColor, 1.0);
}

2. 光源计算着色器(累加单光源贡献)

顶点着色器(渲染全屏四边形)

#version 400 core
in vec2 position;
out vec2 passScreenCoords;
uniform float aspect;

void main() {
    gl_Position = vec4(position, 0.0, 1.0);
    // 转换为适配宽高比的屏幕坐标
    passScreenCoords = vec2(position.x * aspect, position.y);
}

片段着色器

#version 400 core
in vec2 passScreenCoords;
uniform vec3 lightColor;
uniform float lightIntensity;
uniform vec2 lightPosition;
uniform mat4 uProjection;
uniform mat4 uView;
uniform float aspect;

void main() {
    // 将世界空间光源位置转换为NDC空间坐标
    vec4 cameraLightPos = uProjection * uView * vec4(lightPosition, 1.0, 1.0);
    vec2 finalLightPos = vec2(cameraLightPos.x * aspect, cameraLightPos.y);
    
    float lightDistance = distance(finalLightPos, passScreenCoords);
    float diffuse = 0.0;
    
    // 线性衰减(可替换为更真实的平方反比衰减)
    if (lightDistance <= lightIntensity) {
        diffuse = 1.0 - (lightDistance / lightIntensity);
        // 平方反比衰减示例:diffuse = clamp(lightIntensity / (lightDistance * lightDistance), 0.0, 1.0);
    }
    
    // 输出该光源的贡献,后续通过加法混合累加
    gl_FragColor = vec4(lightColor * diffuse, 1.0);
}

3. 最终合成着色器(场景+光照合并)

顶点着色器(全屏四边形)

#version 400 core
in vec2 position;
out vec2 passTexCoords;

void main() {
    gl_Position = vec4(position, 0.0, 1.0);
    // 将NDC坐标转换为纹理采样坐标(0~1范围)
    passTexCoords = (position + 1.0) / 2.0;
}

片段着色器

#version 400 core
in vec2 passTexCoords;
uniform sampler2D sceneSampler;
uniform sampler2D lightSampler;
uniform vec3 ambientColor;

void main() {
    vec3 sceneColor = texture(sceneSampler, passTexCoords).rgb;
    // 环境光+累积的光源贡献
    vec3 totalLight = texture(lightSampler, passTexCoords).rgb + ambientColor;
    // 限制颜色值不超过1.0,避免过曝
    vec3 finalColor = min(sceneColor * totalLight, vec3(1.0));
    gl_FragColor = vec4(finalColor, 1.0);
}

LWJGL端渲染流程关键代码示例

// 初始化FBO(场景缓冲、光照缓冲)
int sceneFBO = glGenFramebuffers();
glBindFramebuffer(GL_FRAMEBUFFER, sceneFBO);
int sceneTexture = glGenTextures();
// ... 配置纹理参数(RGBA、过滤方式等)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sceneTexture, 0);

int lightFBO = glGenFramebuffers();
glBindFramebuffer(GL_FRAMEBUFFER, lightFBO);
int lightTexture = glGenTextures();
// ... 配置纹理参数
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, lightTexture, 0);

// 渲染流程
// 1. 渲染场景到sceneFBO
glBindFramebuffer(GL_FRAMEBUFFER, sceneFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ... 绑定场景着色器,绘制所有场景元素

// 2. 初始化光照缓冲为环境光
glBindFramebuffer(GL_FRAMEBUFFER, lightFBO);
glClearColor(ambientColor.x, ambientColor.y, ambientColor.z, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

// 3. 累加每个光源的贡献
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE); // 加法混合模式
// ... 绑定光源着色器
for (Light light : lights) {
    // 设置光源 uniforms(位置、颜色、强度等)
    glUniform2f(lightLocation, light.getX(), light.getY());
    glUniform3f(lightColorLocation, light.getR(), light.getG(), light.getB());
    glUniform1f(lightIntensityLocation, light.getIntensity());
    // 绘制全屏四边形
    glBindVertexArray(fullScreenVAO);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
glDisable(GL_BLEND);

// 4. 合成场景与光照,输出到屏幕
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ... 绑定合成着色器
glUniform1i(sceneSamplerLocation, 0);
glUniform1i(lightSamplerLocation, 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, sceneTexture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, lightTexture);
// 绘制全屏四边形
glBindVertexArray(fullScreenVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

内容的提问来源于stack exchange,提问作者Philliam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 15:50:23