refactor(model):重构模型数据包结构并增强光源系统

- 将 AnimationLayerData 类从 util 包移动到 data 包
- 将 BufferBuilder 类从 util 包移动到 buffer 包并更新包引用
- 为 LightSource 类添加辉光(Glow)支持及相关字段
- 扩展 LightSourceData 序列化类以包含辉光相关字段
- 新增 MeshData 类用于网格数据的序列化- 更新 Model2D 和 ModelData 的包引用以适应新的类结构
- 移除 ModelData 中重复的内部类定义,统一使用 data 包中的类- 为多个类添加作者信息注解
This commit is contained in:
tzdwindows 7
2025-10-12 08:16:42 +08:00
parent 22c3661d6e
commit fb1db942ed
11 changed files with 1087 additions and 851 deletions

View File

@@ -2,13 +2,12 @@ package com.chuangzhou.vivid2D.render;
import com.chuangzhou.vivid2D.render.model.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.model.buffer.BufferBuilder;
import com.chuangzhou.vivid2D.render.model.util.LightSource;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem; // 引入 PhysicsSystem
import com.chuangzhou.vivid2D.render.model.util.Texture;
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem;
import org.joml.Matrix3f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.opengl.*;
import org.lwjgl.system.MemoryUtil;
@@ -149,78 +148,116 @@ public final class ModelRender {
uniform int uLightsIsAmbient[MAX_LIGHTS];
uniform int uLightCount;
// 辉光相关
uniform int uLightsIsGlow[MAX_LIGHTS];
uniform vec2 uLightsGlowDir[MAX_LIGHTS];
uniform float uLightsGlowIntensity[MAX_LIGHTS];
uniform float uLightsGlowRadius[MAX_LIGHTS];
uniform float uLightsGlowAmount[MAX_LIGHTS];
// 常用衰减系数(可在 shader 内微调)
const float ATT_CONST = 1.0;
const float ATT_LINEAR = 0.09;
const float ATT_QUAD = 0.032;
// 简单 Reinhard tone mapping避免过曝
vec3 toneMap(vec3 color) {
return color / (color + vec3(1.0));
}
void main() {
// 先采样纹理
vec4 tex = texture(uTexture, vTexCoord);
float alpha = tex.a * uOpacity;
if (alpha <= 0.001) discard;
// 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色)
if (uLightCount == 0) {
vec3 base = tex.rgb * uColor.rgb;
// 简单的色调映射(防止数值过大)
base = clamp(base, 0.0, 1.0);
FragColor = vec4(base, alpha);
return;
}
// 基础颜色(纹理 * 部件颜色)
vec3 baseColor = tex.rgb * uColor.rgb;
// 全局环境光基线(可以适度提高以避免全黑
vec3 ambient = vec3(0.06); // 小环境光补偿
// 如果没有光源,仅返回基础颜色(节约性能
if (uLightCount == 0) {
vec3 outCol = clamp(baseColor, 0.0, 1.0);
if (uBlendMode == 1) outCol = tex.rgb + uColor.rgb;
else if (uBlendMode == 2) outCol = tex.rgb * uColor.rgb;
else if (uBlendMode == 3) outCol = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb);
FragColor = vec4(outCol, alpha);
return;
}
// 环境光基线
vec3 ambientBase = vec3(0.06);
vec3 lighting = vec3(0.0);
vec3 glowAccum = vec3(0.0);
vec3 specularAccum = vec3(0.0);
// 累积环境光(来自被标记为环境光的光源
// 累积显式标记为环境光的光源
for (int i = 0; i < uLightCount; ++i) {
if (uLightsIsAmbient[i] == 1) {
lighting += uLightsColor[i] * uLightsIntensity[i];
}
}
// 加上基线环境光
lighting += ambient;
lighting += ambientBase;
// 对每个非环境光计算基于距离的衰减与简单高光
// 对每个非环境光计算物理式衰减 + 漫反射 + 简单高光 + 辉光(若启用)
for (int i = 0; i < uLightCount; ++i) {
if (uLightsIsAmbient[i] == 1) continue;
vec2 toLight = uLightsPos[i] - vWorldPos;
float dist = length(toLight);
// 标准物理式衰减
vec2 toLight2 = uLightsPos[i] - vWorldPos;
float dist = length(toLight2);
// 物理风格衰减
float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist);
// 强度受光源强度和衰减影响
float radiance = uLightsIntensity[i] * attenuation;
// 漫反射在纯2D情景下法线与视线近似固定Z向
// 所以漫反射对所有片元是恒定的。我们用一个基于距离的柔和因子来模拟明暗变化。
float diffuseFactor = clamp(1.0 - (dist * 0.0015), 0.0, 1.0); // 通过调节常数控制半径感觉
// 漫反射(在二维中基于距离模拟衰减的明暗)
// 使用更平滑的距离曲线max(0, 1 - (dist / (radiusApprox)))
float radiusApprox = max(1.0, 1000.0 * attenuation); // 通过衰减估算影响半径
float diffuseFactor = clamp(1.0 - (dist / (radiusApprox + 0.0001)), 0.0, 1.0);
vec3 diff = uLightsColor[i] * radiance * diffuseFactor;
lighting += diff;
// 简单高光(基于视向与反射的大致模拟,产生亮点)
vec3 lightDir3 = normalize(vec3(toLight, 0.0));
// 简单高光(在 2D 中模拟亮点)
vec3 viewDir = vec3(0.0, 0.0, 1.0);
vec3 lightDir3 = normalize(vec3(toLight2, 0.0));
vec3 normal = vec3(0.0, 0.0, 1.0);
vec3 reflectDir = reflect(-lightDir3, normal);
float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); // 16 为高光粗糙度,可调
float specIntensity = 0.2; // 高光强度系数
float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
float specIntensity = 0.25;
specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity;
// 若启用了辉光glow使用高斯风格衰减并支持方向性辉光
if (uLightsIsGlow[i] == 1) {
float glowRadius = max(0.0001, uLightsGlowRadius[i]);
float gdist = dist;
// 高斯分布exp(-(d^2) / (2 * sigma^2))
float sigma = glowRadius * 0.5; // sigma = radius * 0.5(经验值)
float gauss = exp(- (gdist * gdist) / (2.0 * sigma * sigma));
// 方向性因子:如果给出方向,使用方向与片元向量点积来增强朝向一侧的辉光
float dirFactor = 1.0;
vec2 glowDir = uLightsGlowDir[i];
if (length(glowDir) > 0.0001) {
vec2 ndir = normalize(glowDir);
vec2 toFrag = normalize(vWorldPos - uLightsPos[i]);
dirFactor = max(dot(ndir, toFrag), 0.0); // 只在方向半球贡献
}
float gIntensity = uLightsGlowIntensity[i];
float gAmount = uLightsGlowAmount[i];
vec3 glow = uLightsColor[i] * gauss * gIntensity * gAmount * dirFactor;
glowAccum += glow;
}
}
// 限制光照的最大值以避免过曝(可根据场景调整
vec3 totalLighting = min(lighting + specularAccum, vec3(2.0));
// 合并直接光照与高光后进行简单的色调映射(避免过曝
vec3 totalLighting = lighting + specularAccum;
// 防止数值过大,进行 Reinhard tone mapping
vec3 litMapped = toneMap(totalLighting);
vec3 finalColor = baseColor * litMapped;
// 将光照应用到基础颜色
vec3 finalColor = baseColor * totalLighting;
// 将辉光作为屏幕加色(加法混合),然后再做一次 tone map 以稳定输出
finalColor += glowAccum;
finalColor = toneMap(finalColor);
// 支持简单混合模式(保留有行为)
// 支持简单的 blend 模式(保留有行为)
if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb;
else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb;
else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb);
@@ -276,12 +313,19 @@ public final class ModelRender {
com.chuangzhou.vivid2D.render.model.util.LightSource l = lights.get(i);
if (!l.isEnabled()) continue;
// 环境光的 position 在 shader 中不会用于距离计算,但我们也上传(安全)
// 基础属性
setUniformVec2Internal(sp, "uLightsPos[" + idx + "]", l.isAmbient() ? new org.joml.Vector2f(0f, 0f) : l.getPosition());
setUniformVec3Internal(sp, "uLightsColor[" + idx + "]", l.getColor());
setUniformFloatInternal(sp, "uLightsIntensity[" + idx + "]", l.getIntensity());
setUniformIntInternal(sp, "uLightsIsAmbient[" + idx + "]", l.isAmbient() ? 1 : 0);
// 辉光相关(如果没有被设置也安全地上传默认值)
setUniformIntInternal(sp, "uLightsIsGlow[" + idx + "]", l.isGlow() ? 1 : 0);
setUniformVec2Internal(sp, "uLightsGlowDir[" + idx + "]", l.getGlowDirection() != null ? l.getGlowDirection() : new org.joml.Vector2f(0f, 0f));
setUniformFloatInternal(sp, "uLightsGlowIntensity[" + idx + "]", l.getGlowIntensity());
setUniformFloatInternal(sp, "uLightsGlowRadius[" + idx + "]", l.getGlowRadius());
setUniformFloatInternal(sp, "uLightsGlowAmount[" + idx + "]", l.getGlowAmount());
idx++;
}
@@ -292,9 +336,15 @@ public final class ModelRender {
for (int i = idx; i < 8; i++) {
setUniformFloatInternal(sp, "uLightsIntensity[" + i + "]", 0f);
setUniformIntInternal(sp, "uLightsIsAmbient[" + i + "]", 0);
// color/pos 不严格必要,但清零更稳健
setUniformVec3Internal(sp, "uLightsColor[" + i + "]", new org.joml.Vector3f(0f, 0f, 0f));
setUniformVec2Internal(sp, "uLightsPos[" + i + "]", new org.joml.Vector2f(0f, 0f));
// 关闭辉光槽
setUniformIntInternal(sp, "uLightsIsGlow[" + i + "]", 0);
setUniformVec2Internal(sp, "uLightsGlowDir[" + i + "]", new org.joml.Vector2f(0f, 0f));
setUniformFloatInternal(sp, "uLightsGlowIntensity[" + i + "]", 0f);
setUniformFloatInternal(sp, "uLightsGlowRadius[" + i + "]", 0f);
setUniformFloatInternal(sp, "uLightsGlowAmount[" + i + "]", 0f);
}
}
@@ -462,8 +512,8 @@ public final class ModelRender {
if (!light.isEnabled()) continue;
// 绘制光源位置
com.chuangzhou.vivid2D.render.util.BufferBuilder bb =
new com.chuangzhou.vivid2D.render.util.BufferBuilder(1 * 4);
BufferBuilder bb =
new BufferBuilder(1 * 4);
bb.begin(GL11.GL_POINTS, 1);
bb.vertex(light.getPosition().x, light.getPosition().y, 0.5f, 0.5f);
bb.end();
@@ -569,7 +619,7 @@ public final class ModelRender {
*/
private static void drawCircleColliderWire(Vector2f center, float radius) {
int segments = Math.max(8, CIRCLE_SEGMENTS);
com.chuangzhou.vivid2D.render.util.BufferBuilder bb = new com.chuangzhou.vivid2D.render.util.BufferBuilder(segments * 4);
BufferBuilder bb = new BufferBuilder(segments * 4);
bb.begin(GL11.GL_LINE_LOOP, segments);
for (int i = 0; i < segments; i++) {
double ang = 2.0 * Math.PI * i / segments;
@@ -587,7 +637,7 @@ public final class ModelRender {
private static void drawRectangleColliderWire(Vector2f center, float width, float height) {
float halfW = width / 2.0f;
float halfH = height / 2.0f;
com.chuangzhou.vivid2D.render.util.BufferBuilder bb = new com.chuangzhou.vivid2D.render.util.BufferBuilder(4 * 4);
BufferBuilder bb = new BufferBuilder(4 * 4);
bb.begin(GL11.GL_LINE_LOOP, 4);
bb.vertex(center.x - halfW, center.y - halfH, 0.5f, 0.5f);
bb.vertex(center.x + halfW, center.y - halfH, 0.5f, 0.5f);