5448 字
27 分钟
计算机图形学笔记(六):现代图形学前沿

计算机图形学笔记(六):现代图形学前沿#

主要是一些前沿技术

目录#

  1. 实时渲染优化技术 - LOD、遮挡剔除与GPU优化
  2. 基于物理的渲染(PBR) - 微表面理论与材质建模
  3. 体积渲染与参与介质 - 云雾烟尘的渲染技术
  4. 计算着色器与GPGPU - 并行计算在图形学中的应用

实时渲染优化技术#

25.1 层次细节(LOD)技术#

25.1.1 LOD的数学基础#

距离基础LODLODlevel=log2(distancebase_distance)LOD_{level} = \left\lfloor \log_2\left(\frac{distance}{base\_distance}\right) \right\rfloor

其中:

  • distancedistance 是物体到相机的距离
  • base_distancebase\_distance 是基准距离
  • LODlevelLOD_{level} 是细节层次等级

屏幕空间误差度量errorscreen=errorworld×focal_lengthdistance×pixel_sizeerror_{screen} = \frac{error_{world} \times focal\_length}{distance \times pixel\_size}

25.1.2 几何LOD实现#

class GeometricLOD {
private:
struct LODLevel {
Mesh mesh;
float distance_threshold;
int triangle_count;
float geometric_error;
};
std::vector<LODLevel> lod_levels;
public:
void generate_lod_chain(const Mesh& original_mesh, int num_levels) {
lod_levels.clear();
lod_levels.resize(num_levels);
// 原始网格作为LOD 0
lod_levels[0] = {original_mesh, 0.0f,
original_mesh.triangle_count(), 0.0f};
// 生成简化版本
for (int i = 1; i < num_levels; ++i) {
float reduction_ratio = std::pow(0.5f, i);
lod_levels[i].mesh = simplify_mesh(original_mesh, reduction_ratio);
lod_levels[i].distance_threshold = calculate_distance_threshold(i);
lod_levels[i].triangle_count = lod_levels[i].mesh.triangle_count();
lod_levels[i].geometric_error = calculate_geometric_error(
original_mesh, lod_levels[i].mesh);
}
}
const Mesh& select_lod(const Vector3f& camera_pos, const Vector3f& object_pos) {
float distance = (camera_pos - object_pos).norm();
for (int i = lod_levels.size() - 1; i >= 0; --i) {
if (distance >= lod_levels[i].distance_threshold) {
return lod_levels[i].mesh;
}
}
return lod_levels[0].mesh; // 最高细节
}
private:
Mesh simplify_mesh(const Mesh& mesh, float reduction_ratio) {
// 使用二次误差度量进行网格简化
QuadricErrorMetrics qem(mesh);
return qem.simplify(reduction_ratio);
}
float calculate_distance_threshold(int lod_level) {
// 基于屏幕空间误差的距离阈值
float base_distance = 10.0f;
return base_distance * std::pow(2.0f, lod_level);
}
float calculate_geometric_error(const Mesh& original, const Mesh& simplified) {
// 计算Hausdorff距离作为几何误差
float max_error = 0.0f;
for (const auto& vertex : simplified.vertices) {
float min_distance = std::numeric_limits<float>::max();
for (const auto& orig_vertex : original.vertices) {
float distance = (vertex.position - orig_vertex.position).norm();
min_distance = std::min(min_distance, distance);
}
max_error = std::max(max_error, min_distance);
}
return max_error;
}
};

25.2 遮挡剔除技术#

25.2.1 视锥体剔除#

平面方程ax+by+cz+d=0ax + by + cz + d = 0

点到平面距离distance=ax0+by0+cz0+da2+b2+c2distance = \frac{|ax_0 + by_0 + cz_0 + d|}{\sqrt{a^2 + b^2 + c^2}}

class FrustumCuller {
private:
struct Plane {
Vector3f normal;
float distance;
float distance_to_point(const Vector3f& point) const {
return normal.dot(point) + distance;
}
bool is_point_inside(const Vector3f& point) const {
return distance_to_point(point) >= 0;
}
};
std::array<Plane, 6> frustum_planes; // 左右上下远近
public:
void extract_frustum_planes(const Matrix4f& view_projection_matrix) {
// 从MVP矩阵提取视锥体平面
Matrix4f mvp = view_projection_matrix.transpose();
// 左平面: mvp.row(3) + mvp.row(0)
frustum_planes[0] = extract_plane(mvp.row(3) + mvp.row(0));
// 右平面: mvp.row(3) - mvp.row(0)
frustum_planes[1] = extract_plane(mvp.row(3) - mvp.row(0));
// 下平面: mvp.row(3) + mvp.row(1)
frustum_planes[2] = extract_plane(mvp.row(3) + mvp.row(1));
// 上平面: mvp.row(3) - mvp.row(1)
frustum_planes[3] = extract_plane(mvp.row(3) - mvp.row(1));
// 近平面: mvp.row(3) + mvp.row(2)
frustum_planes[4] = extract_plane(mvp.row(3) + mvp.row(2));
// 远平面: mvp.row(3) - mvp.row(2)
frustum_planes[5] = extract_plane(mvp.row(3) - mvp.row(2));
}
bool is_aabb_visible(const AABB& aabb) const {
for (const auto& plane : frustum_planes) {
// 计算AABB的正顶点(最远点)
Vector3f positive_vertex = aabb.min;
if (plane.normal.x() >= 0) positive_vertex.x() = aabb.max.x();
if (plane.normal.y() >= 0) positive_vertex.y() = aabb.max.y();
if (plane.normal.z() >= 0) positive_vertex.z() = aabb.max.z();
// 如果正顶点在平面外侧,则AABB完全在视锥体外
if (plane.distance_to_point(positive_vertex) < 0) {
return false;
}
}
return true;
}
bool is_sphere_visible(const Vector3f& center, float radius) const {
for (const auto& plane : frustum_planes) {
if (plane.distance_to_point(center) < -radius) {
return false;
}
}
return true;
}
private:
Plane extract_plane(const Vector4f& plane_coeffs) {
Vector3f normal = plane_coeffs.head<3>();
float length = normal.norm();
return {normal / length, plane_coeffs.w() / length};
}
};

25.2.2 遮挡查询#

class OcclusionCuller {
private:
struct OcclusionQuery {
GLuint query_id;
bool result_available;
GLuint sample_count;
OcclusionQuery() {
glGenQueries(1, &query_id);
result_available = false;
sample_count = 0;
}
~OcclusionQuery() {
glDeleteQueries(1, &query_id);
}
};
std::unordered_map<uint32_t, std::unique_ptr<OcclusionQuery>> queries;
public:
void begin_occlusion_test(uint32_t object_id) {
auto& query = get_or_create_query(object_id);
// 开始遮挡查询
glBeginQuery(GL_SAMPLES_PASSED, query->query_id);
// 禁用颜色和深度写入,只测试遮挡
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
}
void end_occlusion_test(uint32_t object_id) {
glEndQuery(GL_SAMPLES_PASSED);
// 恢复渲染状态
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
auto& query = queries[object_id];
query->result_available = false;
}
bool is_object_visible(uint32_t object_id, float visibility_threshold = 0.0f) {
auto it = queries.find(object_id);
if (it == queries.end()) {
return true; // 默认可见
}
auto& query = it->second;
if (!query->result_available) {
GLint available;
glGetQueryObjectiv(query->query_id, GL_QUERY_RESULT_AVAILABLE, &available);
if (available) {
glGetQueryObjectuiv(query->query_id, GL_QUERY_RESULT, &query->sample_count);
query->result_available = true;
} else {
return true; // 结果未准备好,假设可见
}
}
return query->sample_count > visibility_threshold;
}
private:
std::unique_ptr<OcclusionQuery>& get_or_create_query(uint32_t object_id) {
auto it = queries.find(object_id);
if (it == queries.end()) {
queries[object_id] = std::make_unique<OcclusionQuery>();
}
return queries[object_id];
}
};

25.3 GPU性能优化#

25.3.1 批处理优化#

class BatchRenderer {
private:
struct BatchData {
std::vector<Matrix4f> model_matrices;
std::vector<Vector4f> colors;
std::vector<Vector2f> texture_coords;
GLuint vao, vbo, instance_vbo;
int instance_count;
BatchData() : instance_count(0) {
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &instance_vbo);
}
~BatchData() {
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &instance_vbo);
}
};
std::unordered_map<uint32_t, std::unique_ptr<BatchData>> batches;
public:
void add_instance(uint32_t batch_id, const Matrix4f& model_matrix,
const Vector4f& color, const Vector2f& tex_coord) {
auto& batch = get_or_create_batch(batch_id);
batch->model_matrices.push_back(model_matrix);
batch->colors.push_back(color);
batch->texture_coords.push_back(tex_coord);
batch->instance_count++;
}
void render_batch(uint32_t batch_id, const Mesh& mesh, const Shader& shader) {
auto it = batches.find(batch_id);
if (it == batches.end() || it->second->instance_count == 0) {
return;
}
auto& batch = it->second;
// 更新实例数据
update_instance_buffer(*batch);
// 绑定VAO和设置顶点属性
glBindVertexArray(batch->vao);
setup_vertex_attributes(mesh, *batch);
// 使用着色器
shader.use();
// 实例化渲染
glDrawElementsInstanced(GL_TRIANGLES, mesh.index_count(),
GL_UNSIGNED_INT, 0, batch->instance_count);
// 清空批次数据
clear_batch(*batch);
}
private:
std::unique_ptr<BatchData>& get_or_create_batch(uint32_t batch_id) {
auto it = batches.find(batch_id);
if (it == batches.end()) {
batches[batch_id] = std::make_unique<BatchData>();
}
return batches[batch_id];
}
void update_instance_buffer(BatchData& batch) {
glBindBuffer(GL_ARRAY_BUFFER, batch.instance_vbo);
// 计算总数据大小
size_t matrix_size = batch.model_matrices.size() * sizeof(Matrix4f);
size_t color_size = batch.colors.size() * sizeof(Vector4f);
size_t texcoord_size = batch.texture_coords.size() * sizeof(Vector2f);
size_t total_size = matrix_size + color_size + texcoord_size;
// 分配缓冲区
glBufferData(GL_ARRAY_BUFFER, total_size, nullptr, GL_DYNAMIC_DRAW);
// 上传数据
glBufferSubData(GL_ARRAY_BUFFER, 0, matrix_size, batch.model_matrices.data());
glBufferSubData(GL_ARRAY_BUFFER, matrix_size, color_size, batch.colors.data());
glBufferSubData(GL_ARRAY_BUFFER, matrix_size + color_size, texcoord_size,
batch.texture_coords.data());
}
void setup_vertex_attributes(const Mesh& mesh, const BatchData& batch) {
// 设置网格顶点属性
mesh.bind_vertex_attributes();
// 设置实例属性
size_t matrix_size = batch.model_matrices.size() * sizeof(Matrix4f);
size_t color_size = batch.colors.size() * sizeof(Vector4f);
glBindBuffer(GL_ARRAY_BUFFER, batch.instance_vbo);
// 模型矩阵(4个vec4)
for (int i = 0; i < 4; ++i) {
glEnableVertexAttribArray(3 + i);
glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix4f),
(void*)(i * sizeof(Vector4f)));
glVertexAttribDivisor(3 + i, 1);
}
// 颜色
glEnableVertexAttribArray(7);
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(Vector4f),
(void*)matrix_size);
glVertexAttribDivisor(7, 1);
// 纹理坐标
glEnableVertexAttribArray(8);
glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2f),
(void*)(matrix_size + color_size));
glVertexAttribDivisor(8, 1);
}
void clear_batch(BatchData& batch) {
batch.model_matrices.clear();
batch.colors.clear();
batch.texture_coords.clear();
batch.instance_count = 0;
}
};

基于物理的渲染(PBR)#

26.1 PBR理论基础#

26.1.1 渲染方程#

完整渲染方程Lo(p,ωo)=Le(p,ωo)+Ωfr(p,ωi,ωo)Li(p,ωi)(ωin)dωiL_o(p, \omega_o) = L_e(p, \omega_o) + \int_{\Omega} f_r(p, \omega_i, \omega_o) L_i(p, \omega_i) (\omega_i \cdot n) d\omega_i

其中:

  • Lo(p,ωo)L_o(p, \omega_o) 是从点p向方向ωo\omega_o的出射辐射度
  • Le(p,ωo)L_e(p, \omega_o) 是自发光
  • fr(p,ωi,ωo)f_r(p, \omega_i, \omega_o) 是双向反射分布函数(BRDF)
  • Li(p,ωi)L_i(p, \omega_i) 是入射辐射度
  • (ωin)(\omega_i \cdot n) 是Lambert余弦定律

26.1.2 BRDF模型#

Cook-Torrance BRDFfr=kdflambert+ksfcooktorrancef_r = k_d f_{lambert} + k_s f_{cook-torrance}

其中: flambert=cπf_{lambert} = \frac{c}{\pi} fcooktorrance=DFG4(ωon)(ωin)f_{cook-torrance} = \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}

法线分布函数(D)DGGX(n,h,α)=α2π((nh)2(α21)+1)2D_{GGX}(n, h, \alpha) = \frac{\alpha^2}{\pi((n \cdot h)^2(\alpha^2 - 1) + 1)^2}

几何函数(G)G(n,v,l,k)=G1(n,v,k)G1(n,l,k)G(n, v, l, k) = G_1(n, v, k) G_1(n, l, k) G1(n,v,k)=nv(nv)(1k)+kG_1(n, v, k) = \frac{n \cdot v}{(n \cdot v)(1 - k) + k}

菲涅尔项(F)FSchlick(h,v,F0)=F0+(1F0)(1(hv))5F_{Schlick}(h, v, F_0) = F_0 + (1 - F_0)(1 - (h \cdot v))^5

class PBRMaterial {
private:
Vector3f albedo;
float metallic;
float roughness;
float ao; // 环境遮挡
Vector3f F0; // 基础反射率
public:
PBRMaterial(const Vector3f& albedo, float metallic, float roughness, float ao = 1.0f)
: albedo(albedo), metallic(metallic), roughness(roughness), ao(ao) {
// 计算基础反射率
F0 = Vector3f(0.04f, 0.04f, 0.04f); // 非金属默认值
F0 = F0 * (1.0f - metallic) + albedo * metallic;
}
Vector3f evaluate_brdf(const Vector3f& light_dir, const Vector3f& view_dir,
const Vector3f& normal) const {
Vector3f h = (light_dir + view_dir).normalized();
float NdotV = std::max(normal.dot(view_dir), 0.0f);
float NdotL = std::max(normal.dot(light_dir), 0.0f);
float HdotV = std::max(h.dot(view_dir), 0.0f);
float NdotH = std::max(normal.dot(h), 0.0f);
// 法线分布函数 (GGX/Trowbridge-Reitz)
float D = distribution_ggx(NdotH, roughness);
// 几何函数
float G = geometry_smith(normal, view_dir, light_dir, roughness);
// 菲涅尔项
Vector3f F = fresnel_schlick(HdotV, F0);
// Cook-Torrance BRDF
Vector3f numerator = D * G * F;
float denominator = 4.0f * NdotV * NdotL + 0.0001f; // 防止除零
Vector3f specular = numerator / denominator;
// 漫反射部分
Vector3f kS = F;
Vector3f kD = Vector3f(1.0f, 1.0f, 1.0f) - kS;
kD *= 1.0f - metallic; // 金属没有漫反射
Vector3f diffuse = kD * albedo / M_PI;
return (diffuse + specular) * NdotL;
}
private:
float distribution_ggx(float NdotH, float roughness) const {
float a = roughness * roughness;
float a2 = a * a;
float NdotH2 = NdotH * NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0f) + 1.0f);
denom = M_PI * denom * denom;
return num / denom;
}
float geometry_schlick_ggx(float NdotV, float roughness) const {
float r = (roughness + 1.0f);
float k = (r * r) / 8.0f;
float num = NdotV;
float denom = NdotV * (1.0f - k) + k;
return num / denom;
}
float geometry_smith(const Vector3f& N, const Vector3f& V,
const Vector3f& L, float roughness) const {
float NdotV = std::max(N.dot(V), 0.0f);
float NdotL = std::max(N.dot(L), 0.0f);
float ggx2 = geometry_schlick_ggx(NdotV, roughness);
float ggx1 = geometry_schlick_ggx(NdotL, roughness);
return ggx1 * ggx2;
}
Vector3f fresnel_schlick(float cosTheta, const Vector3f& F0) const {
return F0 + (Vector3f(1.0f, 1.0f, 1.0f) - F0) *
std::pow(std::clamp(1.0f - cosTheta, 0.0f, 1.0f), 5.0f);
}
};

26.2 基于图像的光照(IBL)#

26.2.1 环境贴图#

class IBLRenderer {
private:
GLuint environment_map;
GLuint irradiance_map;
GLuint prefilter_map;
GLuint brdf_lut;
public:
void setup_ibl(const std::string& hdr_path) {
// 加载HDR环境贴图
load_hdr_environment(hdr_path);
// 生成辐照度贴图
generate_irradiance_map();
// 生成预过滤环境贴图
generate_prefilter_map();
// 生成BRDF查找表
generate_brdf_lut();
}
Vector3f sample_environment_lighting(const Vector3f& normal, const Vector3f& view_dir,
const PBRMaterial& material) const {
Vector3f reflection = reflect(-view_dir, normal);
// 漫反射部分:从辐照度贴图采样
Vector3f irradiance = sample_irradiance_map(normal);
Vector3f diffuse = irradiance * material.get_albedo();
// 镜面反射部分:从预过滤贴图采样
float roughness = material.get_roughness();
Vector3f prefiltered_color = sample_prefilter_map(reflection, roughness);
// BRDF积分
float NdotV = std::max(normal.dot(view_dir), 0.0f);
Vector2f brdf_sample = sample_brdf_lut(NdotV, roughness);
Vector3f F0 = material.get_F0();
Vector3f specular = prefiltered_color * (F0 * brdf_sample.x() + brdf_sample.y());
// 组合结果
Vector3f kS = fresnel_schlick_roughness(NdotV, F0, roughness);
Vector3f kD = Vector3f(1.0f, 1.0f, 1.0f) - kS;
kD *= 1.0f - material.get_metallic();
return kD * diffuse + specular;
}
private:
void load_hdr_environment(const std::string& path) {
// 加载HDR图像
int width, height, channels;
float* data = stbi_loadf(path.c_str(), &width, &height, &channels, 0);
if (data) {
glGenTextures(1, &environment_map);
glBindTexture(GL_TEXTURE_2D, environment_map);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
}
void generate_irradiance_map() {
// 创建立方体贴图
glGenTextures(1, &irradiance_map);
glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance_map);
for (unsigned int i = 0; i < 6; ++i) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F,
32, 32, 0, GL_RGB, GL_FLOAT, nullptr);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 使用计算着色器或渲染到立方体贴图的方式生成辐照度贴图
render_irradiance_convolution();
}
void generate_prefilter_map() {
glGenTextures(1, &prefilter_map);
glBindTexture(GL_TEXTURE_CUBE_MAP, prefilter_map);
for (unsigned int i = 0; i < 6; ++i) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F,
128, 128, 0, GL_RGB, GL_FLOAT, nullptr);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
// 为每个mip级别渲染不同粗糙度的预过滤贴图
render_prefilter_convolution();
}
void generate_brdf_lut() {
glGenTextures(1, &brdf_lut);
glBindTexture(GL_TEXTURE_2D, brdf_lut);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, 512, 512, 0, GL_RG, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 渲染BRDF积分查找表
render_brdf_integration();
}
Vector3f fresnel_schlick_roughness(float cosTheta, const Vector3f& F0, float roughness) const {
return F0 + (std::max(Vector3f(1.0f - roughness, 1.0f - roughness, 1.0f - roughness), F0) - F0) *
std::pow(std::clamp(1.0f - cosTheta, 0.0f, 1.0f), 5.0f);
}
};

体积渲染与参与介质#

27.1 体积渲染理论#

27.1.1 体积渲染方程#

体积渲染积分方程L(x,ω)=0dT(x,x+tω)σs(x+tω)4πp(x+tω,ω,ω)L(x+tω,ω)dωdt+T(x,x+dω)L(x+dω,ω)L(x, \omega) = \int_0^d T(x, x + t\omega) \sigma_s(x + t\omega) \int_{4\pi} p(x + t\omega, \omega', \omega) L(x + t\omega, \omega') d\omega' dt + T(x, x + d\omega) L(x + d\omega, \omega)

其中:

  • T(x,y)T(x, y) 是透射率函数
  • σs\sigma_s 是散射系数
  • p(ω,ω)p(\omega', \omega) 是相位函数
  • dd 是光线传播距离

透射率函数T(x,y)=exp(0yxσt(x+syxyx)ds)T(x, y) = \exp\left(-\int_0^{||y-x||} \sigma_t(x + s \cdot \frac{y-x}{||y-x||}) ds\right)

相位函数(Henyey-Greenstein)p(cosθ)=1g24π(1+g22gcosθ)3/2p(\cos\theta) = \frac{1 - g^2}{4\pi(1 + g^2 - 2g\cos\theta)^{3/2}}

其中g[1,1]g \in [-1, 1]是各向异性参数。

class VolumeRenderer {
private:
struct VolumeProperties {
float density; // 密度
float absorption; // 吸收系数
float scattering; // 散射系数
float anisotropy; // 各向异性参数g
Vector3f albedo; // 散射反照率
float extinction() const { return absorption + scattering; }
};
struct VolumeData {
std::vector<float> density_grid;
int width, height, depth;
Vector3f bounds_min, bounds_max;
float sample_density(const Vector3f& pos) const {
// 三线性插值采样密度
Vector3f normalized_pos = (pos - bounds_min).cwiseQuotient(bounds_max - bounds_min);
if (normalized_pos.x() < 0 || normalized_pos.x() > 1 ||
normalized_pos.y() < 0 || normalized_pos.y() > 1 ||
normalized_pos.z() < 0 || normalized_pos.z() > 1) {
return 0.0f;
}
return trilinear_interpolate(normalized_pos);
}
private:
float trilinear_interpolate(const Vector3f& pos) const {
float x = pos.x() * (width - 1);
float y = pos.y() * (height - 1);
float z = pos.z() * (depth - 1);
int x0 = static_cast<int>(x), x1 = std::min(x0 + 1, width - 1);
int y0 = static_cast<int>(y), y1 = std::min(y0 + 1, height - 1);
int z0 = static_cast<int>(z), z1 = std::min(z0 + 1, depth - 1);
float fx = x - x0, fy = y - y0, fz = z - z0;
auto get_density = [this](int x, int y, int z) {
return density_grid[z * width * height + y * width + x];
};
// 三线性插值
float c000 = get_density(x0, y0, z0);
float c001 = get_density(x0, y0, z1);
float c010 = get_density(x0, y1, z0);
float c011 = get_density(x0, y1, z1);
float c100 = get_density(x1, y0, z0);
float c101 = get_density(x1, y0, z1);
float c110 = get_density(x1, y1, z0);
float c111 = get_density(x1, y1, z1);
float c00 = c000 * (1 - fx) + c100 * fx;
float c01 = c001 * (1 - fx) + c101 * fx;
float c10 = c010 * (1 - fx) + c110 * fx;
float c11 = c011 * (1 - fx) + c111 * fx;
float c0 = c00 * (1 - fy) + c10 * fy;
float c1 = c01 * (1 - fy) + c11 * fy;
return c0 * (1 - fz) + c1 * fz;
}
};
VolumeData volume_data;
VolumeProperties volume_props;
public:
Vector3f render_volume_ray(const Ray& ray, float t_min, float t_max,
const std::vector<Light>& lights) const {
Vector3f color(0, 0, 0);
float transmittance = 1.0f;
// 光线步进参数
float step_size = 0.1f;
int num_steps = static_cast<int>((t_max - t_min) / step_size);
for (int i = 0; i < num_steps; ++i) {
float t = t_min + i * step_size;
Vector3f pos = ray.origin + t * ray.direction;
// 采样体积密度
float density = volume_data.sample_density(pos);
if (density <= 0.0f) continue;
// 计算体积属性
float extinction = volume_props.extinction() * density;
float scattering = volume_props.scattering * density;
// 计算透射率衰减
float step_transmittance = std::exp(-extinction * step_size);
// 计算散射光照
Vector3f scattered_light = calculate_in_scattering(pos, -ray.direction, lights, density);
// 累积颜色
color += transmittance * (1.0f - step_transmittance) * scattered_light;
// 更新透射率
transmittance *= step_transmittance;
// 早期终止优化
if (transmittance < 0.01f) break;
}
return color;
}
private:
Vector3f calculate_in_scattering(const Vector3f& pos, const Vector3f& view_dir,
const std::vector<Light>& lights, float density) const {
Vector3f scattered_light(0, 0, 0);
for (const auto& light : lights) {
Vector3f light_dir = (light.position - pos).normalized();
float light_distance = (light.position - pos).norm();
// 计算到光源的透射率
float light_transmittance = calculate_transmittance(pos, light.position);
// 相位函数
float cos_theta = view_dir.dot(light_dir);
float phase = henyey_greenberg_phase(cos_theta, volume_props.anisotropy);
// 光照衰减
float attenuation = 1.0f / (light_distance * light_distance);
// 散射贡献
Vector3f light_contribution = light.color * light.intensity * attenuation *
light_transmittance * phase * volume_props.scattering * density;
scattered_light += light_contribution * volume_props.albedo;
}
return scattered_light;
}
float calculate_transmittance(const Vector3f& start, const Vector3f& end) const {
Vector3f direction = (end - start).normalized();
float distance = (end - start).norm();
float transmittance = 1.0f;
float step_size = 0.1f;
int num_steps = static_cast<int>(distance / step_size);
for (int i = 0; i < num_steps; ++i) {
float t = i * step_size;
Vector3f pos = start + t * direction;
float density = volume_data.sample_density(pos);
float extinction = volume_props.extinction() * density;
transmittance *= std::exp(-extinction * step_size);
if (transmittance < 0.01f) break;
}
return transmittance;
}
float henyey_greenberg_phase(float cos_theta, float g) const {
float g2 = g * g;
float denom = 1.0f + g2 - 2.0f * g * cos_theta;
return (1.0f - g2) / (4.0f * M_PI * std::pow(denom, 1.5f));
}
};

27.2 云渲染#

class CloudRenderer {
private:
struct CloudLayer {
float altitude_min, altitude_max;
float density_multiplier;
float coverage;
Vector2f wind_direction;
float wind_speed;
// 噪声参数
float noise_scale;
float detail_scale;
int octaves;
};
std::vector<CloudLayer> cloud_layers;
GLuint noise_texture_3d;
GLuint weather_texture;
public:
void setup_cloud_system() {
// 生成3D噪声纹理
generate_3d_noise_texture();
// 生成天气贴图
generate_weather_texture();
// 设置云层
setup_cloud_layers();
}
Vector4f render_clouds(const Ray& ray, float t_min, float t_max,
const Vector3f& sun_direction) const {
Vector3f color(0, 0, 0);
float alpha = 0.0f;
// 光线步进
float step_size = 100.0f; // 云渲染使用较大步长
int num_steps = static_cast<int>((t_max - t_min) / step_size);
for (int i = 0; i < num_steps; ++i) {
float t = t_min + i * step_size;
Vector3f pos = ray.origin + t * ray.direction;
// 采样云密度
float cloud_density = sample_cloud_density(pos);
if (cloud_density <= 0.0f) continue;
// 计算光照
Vector3f light_color = calculate_cloud_lighting(pos, sun_direction, cloud_density);
// 透明度混合
float step_alpha = 1.0f - std::exp(-cloud_density * step_size * 0.01f);
color += (1.0f - alpha) * step_alpha * light_color;
alpha += (1.0f - alpha) * step_alpha;
// 早期终止
if (alpha > 0.99f) break;
}
return Vector4f(color.x(), color.y(), color.z(), alpha);
}
private:
float sample_cloud_density(const Vector3f& pos) const {
float total_density = 0.0f;
for (const auto& layer : cloud_layers) {
if (pos.y() < layer.altitude_min || pos.y() > layer.altitude_max) {
continue;
}
// 高度衰减
float height_fraction = (pos.y() - layer.altitude_min) /
(layer.altitude_max - layer.altitude_min);
float height_gradient = 4.0f * height_fraction * (1.0f - height_fraction);
// 基础噪声
Vector3f noise_coord = pos * layer.noise_scale;
float base_noise = sample_3d_noise(noise_coord);
// 细节噪声
Vector3f detail_coord = pos * layer.detail_scale;
float detail_noise = sample_3d_noise(detail_coord);
// 组合噪声
float combined_noise = base_noise * 0.7f + detail_noise * 0.3f;
// 应用覆盖率和高度梯度
float density = std::max(0.0f, combined_noise - (1.0f - layer.coverage));
density *= height_gradient * layer.density_multiplier;
total_density += density;
}
return std::min(total_density, 1.0f);
}
Vector3f calculate_cloud_lighting(const Vector3f& pos, const Vector3f& sun_dir,
float density) const {
// 基础环境光
Vector3f ambient = Vector3f(0.6f, 0.7f, 0.8f) * 0.3f;
// 太阳光散射
float sun_transmittance = calculate_sun_transmittance(pos, sun_dir);
Vector3f sun_color = Vector3f(1.0f, 0.9f, 0.7f) * sun_transmittance;
// Henyey-Greenberg相位函数(云的前向散射)
float cos_theta = -sun_dir.dot(Vector3f(0, -1, 0)); // 假设视线向下
float phase = henyey_greenberg_phase(cos_theta, 0.3f); // 轻微前向散射
// 组合光照
Vector3f total_light = ambient + sun_color * phase;
// 应用密度
return total_light * density;
}
float calculate_sun_transmittance(const Vector3f& pos, const Vector3f& sun_dir) const {
// 简化的太阳光透射率计算
float transmittance = 1.0f;
float step_size = 50.0f;
for (int i = 0; i < 10; ++i) { // 有限步数的光线行进
Vector3f sample_pos = pos + i * step_size * sun_dir;
float density = sample_cloud_density(sample_pos);
transmittance *= std::exp(-density * step_size * 0.005f);
if (transmittance < 0.1f) break;
}
return transmittance;
}
float sample_3d_noise(const Vector3f& coord) const {
// 这里应该从3D噪声纹理采样
// 简化实现:使用程序化噪声
return (std::sin(coord.x()) * std::cos(coord.y()) * std::sin(coord.z()) + 1.0f) * 0.5f;
}
void generate_3d_noise_texture() {
// 生成3D Perlin噪声纹理
const int size = 128;
std::vector<float> noise_data(size * size * size);
for (int z = 0; z < size; ++z) {
for (int y = 0; y < size; ++y) {
for (int x = 0; x < size; ++x) {
Vector3f coord(x / float(size), y / float(size), z / float(size));
int index = z * size * size + y * size + x;
noise_data[index] = generate_perlin_noise(coord * 8.0f);
}
}
}
glGenTextures(1, &noise_texture_3d);
glBindTexture(GL_TEXTURE_3D, noise_texture_3d);
glTexImage3D(GL_TEXTURE_3D, 0, GL_R16F, size, size, size, 0, GL_RED, GL_FLOAT, noise_data.data());
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
}
float generate_perlin_noise(const Vector3f& coord) const {
// 简化的Perlin噪声实现
return (std::sin(coord.x()) * std::cos(coord.y()) * std::sin(coord.z()) + 1.0f) * 0.5f;
}
float henyey_greenberg_phase(float cos_theta, float g) const {
float g2 = g * g;
float denom = 1.0f + g2 - 2.0f * g * cos_theta;
return (1.0f - g2) / (4.0f * M_PI * std::pow(denom, 1.5f));
}
};

计算着色器与GPGPU#

28.1 计算着色器基础#

28.1.1 工作组和线程模型#

工作组大小

  • 本地工作组大小:(local_size_x,local_size_y,local_size_z)(local\_size\_x, local\_size\_y, local\_size\_z)
  • 全局工作组数量:(num_groups_x,num_groups_y,num_groups_z)(num\_groups\_x, num\_groups\_y, num\_groups\_z)
  • 总线程数:total_threads=local_size×num_groupstotal\_threads = local\_size \times num\_groups

线程索引计算global_id=local_id+group_id×local_sizeglobal\_id = local\_id + group\_id \times local\_size

class ComputeShaderManager {
private:
struct ComputeProgram {
GLuint program_id;
GLuint shader_id;
std::unordered_map<std::string, GLint> uniform_locations;
ComputeProgram() : program_id(0), shader_id(0) {}
~ComputeProgram() {
if (shader_id) glDeleteShader(shader_id);
if (program_id) glDeleteProgram(program_id);
}
};
std::unordered_map<std::string, std::unique_ptr<ComputeProgram>> programs;
public:
bool create_compute_program(const std::string& name, const std::string& source) {
auto program = std::make_unique<ComputeProgram>();
// 创建计算着色器
program->shader_id = glCreateShader(GL_COMPUTE_SHADER);
const char* source_ptr = source.c_str();
glShaderSource(program->shader_id, 1, &source_ptr, nullptr);
glCompileShader(program->shader_id);
// 检查编译错误
GLint success;
glGetShaderiv(program->shader_id, GL_COMPILE_STATUS, &success);
if (!success) {
char info_log[512];
glGetShaderInfoLog(program->shader_id, 512, nullptr, info_log);
std::cerr << "计算着色器编译失败: " << info_log << std::endl;
return false;
}
// 创建程序
program->program_id = glCreateProgram();
glAttachShader(program->program_id, program->shader_id);
glLinkProgram(program->program_id);
// 检查链接错误
glGetProgramiv(program->program_id, GL_LINK_STATUS, &success);
if (!success) {
char info_log[512];
glGetProgramInfoLog(program->program_id, 512, nullptr, info_log);
std::cerr << "计算着色器程序链接失败: " << info_log << std::endl;
return false;
}
programs[name] = std::move(program);
return true;
}
void dispatch_compute(const std::string& program_name,
GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) {
auto it = programs.find(program_name);
if (it == programs.end()) {
std::cerr << "计算着色器程序未找到: " << program_name << std::endl;
return;
}
glUseProgram(it->second->program_id);
glDispatchCompute(num_groups_x, num_groups_y, num_groups_z);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
}
void set_uniform(const std::string& program_name, const std::string& uniform_name,
float value) {
auto it = programs.find(program_name);
if (it == programs.end()) return;
auto& program = it->second;
auto uniform_it = program->uniform_locations.find(uniform_name);
GLint location;
if (uniform_it == program->uniform_locations.end()) {
location = glGetUniformLocation(program->program_id, uniform_name.c_str());
program->uniform_locations[uniform_name] = location;
} else {
location = uniform_it->second;
}
if (location != -1) {
glUseProgram(program->program_id);
glUniform1f(location, value);
}
}
};

28.2 并行算法实现#

28.2.1 并行前缀和(Prefix Sum)#

class ParallelPrefixSum {
private:
ComputeShaderManager compute_manager;
GLuint input_buffer, output_buffer, temp_buffer;
const std::string upsweep_shader = R"(
#version 430
layout(local_size_x = 256) in;
layout(std430, binding = 0) restrict buffer InputBuffer {
float input_data[];
};
layout(std430, binding = 1) restrict buffer TempBuffer {
float temp_data[];
};
uniform int step;
uniform int n;
void main() {
uint index = gl_GlobalInvocationID.x;
uint stride = 1u << step;
uint read_index = (index + 1u) * stride * 2u - 1u;
if (read_index < n) {
uint write_index = read_index + stride;
if (write_index < n) {
temp_data[write_index] += temp_data[read_index];
}
}
}
)";
const std::string downsweep_shader = R"(
#version 430
layout(local_size_x = 256) in;
layout(std430, binding = 1) restrict buffer TempBuffer {
float temp_data[];
};
layout(std430, binding = 2) restrict buffer OutputBuffer {
float output_data[];
};
uniform int step;
uniform int n;
void main() {
uint index = gl_GlobalInvocationID.x;
uint stride = 1u << (step + 1);
uint read_index = (index + 1u) * stride - 1u;
if (read_index < n) {
uint write_index = read_index + (stride >> 1);
if (write_index < n) {
float temp = temp_data[read_index];
temp_data[read_index] = temp_data[write_index];
temp_data[write_index] += temp;
}
}
}
)";
public:
void setup(int max_size) {
// 创建缓冲区
glGenBuffers(1, &input_buffer);
glGenBuffers(1, &output_buffer);
glGenBuffers(1, &temp_buffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, max_size * sizeof(float), nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, max_size * sizeof(float), nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, temp_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, max_size * sizeof(float), nullptr, GL_DYNAMIC_DRAW);
// 编译着色器
compute_manager.create_compute_program("upsweep", upsweep_shader);
compute_manager.create_compute_program("downsweep", downsweep_shader);
}
void compute_prefix_sum(const std::vector<float>& input, std::vector<float>& output) {
int n = input.size();
output.resize(n);
// 上传输入数据
glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, n * sizeof(float), input.data());
// 复制到临时缓冲区
glBindBuffer(GL_COPY_READ_BUFFER, input_buffer);
glBindBuffer(GL_COPY_WRITE_BUFFER, temp_buffer);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, n * sizeof(float));
// 绑定缓冲区
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, input_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, temp_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, output_buffer);
// Up-sweep阶段
int steps = static_cast<int>(std::ceil(std::log2(n)));
for (int step = 0; step < steps; ++step) {
compute_manager.set_uniform("upsweep", "step", static_cast<float>(step));
compute_manager.set_uniform("upsweep", "n", static_cast<float>(n));
int num_groups = (n + 255) / 256;
compute_manager.dispatch_compute("upsweep", num_groups, 1, 1);
}
// 清零最后一个元素
float zero = 0.0f;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, temp_buffer);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, (n - 1) * sizeof(float), sizeof(float), &zero);
// Down-sweep阶段
for (int step = steps - 1; step >= 0; --step) {
compute_manager.set_uniform("downsweep", "step", static_cast<float>(step));
compute_manager.set_uniform("downsweep", "n", static_cast<float>(n));
int num_groups = (n + 255) / 256;
compute_manager.dispatch_compute("downsweep", num_groups, 1, 1);
}
// 复制结果
glBindBuffer(GL_COPY_READ_BUFFER, temp_buffer);
glBindBuffer(GL_COPY_WRITE_BUFFER, output_buffer);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, n * sizeof(float));
// 下载结果
glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer);
float* result = static_cast<float*>(glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY));
std::copy(result, result + n, output.begin());
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
};

28.2.2 GPU粒子系统#

class GPUParticleSystem {
private:
struct Particle {
Vector3f position;
float life;
Vector3f velocity;
float size;
Vector4f color;
Vector3f acceleration;
float padding;
};
GLuint particle_buffer;
GLuint counter_buffer;
GLuint indirect_buffer;
ComputeShaderManager compute_manager;
const std::string update_shader = R"(
#version 430
layout(local_size_x = 256) in;
struct Particle {
vec3 position;
float life;
vec3 velocity;
float size;
vec4 color;
vec3 acceleration;
float padding;
};
layout(std430, binding = 0) restrict buffer ParticleBuffer {
Particle particles[];
};
layout(std430, binding = 1) restrict buffer CounterBuffer {
uint alive_count;
uint dead_count;
uint alive_list[];
};
uniform float delta_time;
uniform vec3 gravity;
uniform float damping;
void main() {
uint index = gl_GlobalInvocationID.x;
if (index >= particles.length()) return;
Particle p = particles[index];
// 更新生命周期
p.life -= delta_time;
if (p.life > 0.0) {
// 更新物理
p.acceleration += gravity;
p.velocity += p.acceleration * delta_time;
p.velocity *= damping;
p.position += p.velocity * delta_time;
// 更新颜色(基于生命周期)
float life_ratio = p.life / 5.0; // 假设最大生命周期为5秒
p.color.a = life_ratio;
// 重置加速度
p.acceleration = vec3(0.0);
particles[index] = p;
// 添加到活跃列表
uint alive_index = atomicAdd(alive_count, 1);
alive_list[alive_index] = index;
} else {
// 粒子死亡
atomicAdd(dead_count, 1);
}
}
)";
const std::string emit_shader = R"(
#version 430
layout(local_size_x = 64) in;
struct Particle {
vec3 position;
float life;
vec3 velocity;
float size;
vec4 color;
vec3 acceleration;
float padding;
};
layout(std430, binding = 0) restrict buffer ParticleBuffer {
Particle particles[];
};
layout(std430, binding = 1) restrict buffer CounterBuffer {
uint alive_count;
uint dead_count;
uint alive_list[];
};
uniform vec3 emitter_position;
uniform vec3 emitter_direction;
uniform float emit_rate;
uniform float delta_time;
uniform uint random_seed;
// 简单的随机数生成器
uint hash(uint x) {
x += (x << 10u);
x ^= (x >> 6u);
x += (x << 3u);
x ^= (x >> 11u);
x += (x << 15u);
return x;
}
float random(uint seed) {
return float(hash(seed)) / 4294967295.0;
}
void main() {
uint index = gl_GlobalInvocationID.x;
// 计算需要发射的粒子数量
uint emit_count = uint(emit_rate * delta_time);
if (index >= emit_count) return;
// 查找死亡的粒子进行重用
if (dead_count > 0) {
uint dead_index = atomicAdd(dead_count, -1) - 1;
if (dead_index < particles.length()) {
uint particle_index = dead_index; // 简化:直接使用索引
// 初始化新粒子
uint seed = random_seed + index * 1000u;
particles[particle_index].position = emitter_position +
vec3(random(seed) - 0.5, random(seed + 1u) - 0.5, random(seed + 2u) - 0.5) * 2.0;
particles[particle_index].life = 3.0 + random(seed + 3u) * 2.0; // 3-5秒生命周期
vec3 random_dir = normalize(vec3(
random(seed + 4u) - 0.5,
random(seed + 5u) - 0.5,
random(seed + 6u) - 0.5
));
particles[particle_index].velocity = emitter_direction + random_dir * 0.5;
particles[particle_index].size = 0.1 + random(seed + 7u) * 0.1;
particles[particle_index].color = vec4(1.0, 0.8, 0.2, 1.0);
particles[particle_index].acceleration = vec3(0.0);
}
}
}
)";
public:
void setup(int max_particles) {
// 创建粒子缓冲区
glGenBuffers(1, &particle_buffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, particle_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, max_particles * sizeof(Particle), nullptr, GL_DYNAMIC_DRAW);
// 创建计数器缓冲区
glGenBuffers(1, &counter_buffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, counter_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, (2 + max_particles) * sizeof(GLuint), nullptr, GL_DYNAMIC_DRAW);
// 初始化计数器
GLuint initial_counts[2] = {0, static_cast<GLuint>(max_particles)};
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, 2 * sizeof(GLuint), initial_counts);
// 编译着色器
compute_manager.create_compute_program("update_particles", update_shader);
compute_manager.create_compute_program("emit_particles", emit_shader);
}
void update(float delta_time, const Vector3f& emitter_pos, const Vector3f& emitter_dir,
float emit_rate) {
// 绑定缓冲区
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, particle_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, counter_buffer);
// 重置活跃计数
GLuint zero = 0;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, counter_buffer);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), &zero);
// 更新粒子
compute_manager.set_uniform("update_particles", "delta_time", delta_time);
compute_manager.set_uniform("update_particles", "damping", 0.98f);
compute_manager.dispatch_compute("update_particles", (10000 + 255) / 256, 1, 1);
// 发射新粒子
compute_manager.set_uniform("emit_particles", "delta_time", delta_time);
compute_manager.set_uniform("emit_particles", "emit_rate", emit_rate);
compute_manager.set_uniform("emit_particles", "random_seed",
static_cast<float>(std::rand()));
compute_manager.dispatch_compute("emit_particles", (64 + 63) / 64, 1, 1);
}
GLuint get_particle_buffer() const { return particle_buffer; }
GLuint get_counter_buffer() const { return counter_buffer; }
};
计算机图形学笔记(六):现代图形学前沿
https://kyc001.github.io/posts/计算机图形学笔记六/
作者
kyc001
发布于
2025-07-25
许可协议
CC BY-NC-SA 4.0