5448 字
27 分钟
计算机图形学笔记(六):现代图形学前沿
计算机图形学笔记(六):现代图形学前沿
主要是一些前沿技术
目录
- 实时渲染优化技术 - LOD、遮挡剔除与GPU优化
- 基于物理的渲染(PBR) - 微表面理论与材质建模
- 体积渲染与参与介质 - 云雾烟尘的渲染技术
- 计算着色器与GPGPU - 并行计算在图形学中的应用
实时渲染优化技术
25.1 层次细节(LOD)技术
25.1.1 LOD的数学基础
距离基础LOD:
其中:
- 是物体到相机的距离
- 是基准距离
- 是细节层次等级
屏幕空间误差度量:
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 视锥体剔除
平面方程:
点到平面距离:
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 渲染方程
完整渲染方程:
其中:
- 是从点p向方向的出射辐射度
- 是自发光
- 是双向反射分布函数(BRDF)
- 是入射辐射度
- 是Lambert余弦定律
26.1.2 BRDF模型
Cook-Torrance BRDF:
其中:
法线分布函数(D):
几何函数(G):
菲涅尔项(F):
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 体积渲染方程
体积渲染积分方程:
其中:
- 是透射率函数
- 是散射系数
- 是相位函数
- 是光线传播距离
透射率函数:
相位函数(Henyey-Greenstein):
其中是各向异性参数。
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 工作组和线程模型
工作组大小:
- 本地工作组大小:
- 全局工作组数量:
- 总线程数:
线程索引计算:
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/计算机图形学笔记六/