Fix&Add:修复最多只能上传256个material的虫,添加BVH上传代码和光线追踪实现
parent
03baf12976
commit
d0d97032db
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -225,7 +225,7 @@ void setup_cornell_box() {
|
|||
g_scene->add_mesh(tall_box);
|
||||
|
||||
// Short box (metal, right side)
|
||||
auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), metal_id);
|
||||
auto short_box = create_box(Vec3(0.2f, -room_size, 0.2f), Vec3(0.9f, -0.4f, 0.9f), /*metal_id*/white_id);
|
||||
short_box->upload_to_gpu();
|
||||
g_scene->add_mesh(short_box);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ constexpr int DEFAULT_SPP = 1;
|
|||
constexpr int GBUFFER_POSITION = 0;
|
||||
constexpr int GBUFFER_NORMAL = 1;
|
||||
constexpr int GBUFFER_ALBEDO = 2;
|
||||
constexpr int GBUFFER_COUNT = 3;
|
||||
constexpr int GBUFFER_MATERIAL = 3;
|
||||
constexpr int GBUFFER_MATERIAL_ID = 4;
|
||||
constexpr int GBUFFER_COUNT = 5;
|
||||
|
||||
/// @brief Compute shader work group size
|
||||
constexpr int COMPUTE_GROUP_SIZE_X = 16;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,24 @@ struct BVHNode {
|
|||
uint count_; // 0 for interior node, >0 for leaf node
|
||||
};
|
||||
|
||||
/// @brief GPU-friendly BVH node layout (std430 aligned)
|
||||
struct BVHNodeGpu {
|
||||
Vec4 aabb_min_left_first_; ///< xyz = aabb min, w = left_first (uint)
|
||||
Vec4 aabb_max_count_; ///< xyz = aabb max, w = count (uint, 0 for interior)
|
||||
};
|
||||
|
||||
/// @brief GPU-friendly triangle layout (std430 aligned)
|
||||
struct TriangleGpu {
|
||||
Vec4 v0_material_; ///< xyz = v0, w = material_id (uint)
|
||||
Vec4 v1_; ///< xyz = v1, w = reserved
|
||||
Vec4 v2_; ///< xyz = v2, w = reserved
|
||||
Vec4 n0_; ///< xyz = n0, w = reserved
|
||||
Vec4 n1_; ///< xyz = n1, w = reserved
|
||||
Vec4 n2_; ///< xyz = n2, w = reserved
|
||||
Vec4 uv0_uv1_; ///< xy = uv0, zw = uv1
|
||||
Vec4 uv2_; ///< xy = uv2, zw = reserved
|
||||
};
|
||||
|
||||
/// @brief Bounding Volume Hierarchy for ray tracing acceleration
|
||||
class BVH {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
#version 430 core
|
||||
|
||||
// Material types
|
||||
const uint MATERIAL_DIFFUSE = 0u;
|
||||
const uint MATERIAL_METAL = 1u;
|
||||
const uint MATERIAL_DIELECTRIC = 2u;
|
||||
const uint MATERIAL_EMISSIVE = 3u;
|
||||
|
||||
in VS_OUT {
|
||||
vec3 frag_pos;
|
||||
vec3 normal;
|
||||
|
|
@ -17,33 +11,31 @@ layout(location = 0) out vec4 g_position;
|
|||
layout(location = 1) out vec4 g_normal;
|
||||
layout(location = 2) out vec4 g_albedo;
|
||||
layout(location = 3) out vec4 g_material;
|
||||
layout(location = 4) out uint g_material_id;
|
||||
|
||||
// Material uniforms
|
||||
uniform vec3 u_albedo;
|
||||
uniform float u_metallic;
|
||||
uniform float u_roughness;
|
||||
uniform float u_ior;
|
||||
uniform vec3 u_emission;
|
||||
uniform uint u_material_type;
|
||||
uniform uint u_material_id;
|
||||
|
||||
uniform bool u_has_albedo_map;
|
||||
uniform sampler2D u_albedo_map;
|
||||
|
||||
void main() {
|
||||
// Position
|
||||
g_position = vec4(fs_in.frag_pos, 1.0);
|
||||
|
||||
// Normal
|
||||
vec3 normal = normalize(fs_in.normal);
|
||||
g_normal = vec4(normal, 0.0);
|
||||
vec3 n = normalize(fs_in.normal);
|
||||
g_normal = vec4(n, 0.0);
|
||||
|
||||
// Albedo
|
||||
vec3 albedo = u_albedo;
|
||||
if (u_has_albedo_map) {
|
||||
albedo *= texture(u_albedo_map, fs_in.texcoord).rgb;
|
||||
}
|
||||
g_albedo = vec4(albedo, 1.0);
|
||||
|
||||
// Material properties
|
||||
g_material = vec4(u_metallic, u_roughness, u_ior, float(u_material_type));
|
||||
g_material_id = u_material_id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,35 @@
|
|||
#version 430 core
|
||||
|
||||
// Constants
|
||||
#define PI 3.14159265359
|
||||
#define INV_PI 0.31830988618
|
||||
#define EPSILON 1e-4
|
||||
#define MAX_FLOAT 3.402823466e38
|
||||
#define MAX_RAY_DEPTH 8
|
||||
#define MAX_LIGHTS 16
|
||||
#define RR_THRESHOLD 0.1
|
||||
|
||||
// Material types
|
||||
#define MATERIAL_DIFFUSE 0
|
||||
#define MATERIAL_METAL 1
|
||||
#define MATERIAL_DIELECTRIC 2
|
||||
#define MATERIAL_EMISSIVE 3
|
||||
|
||||
// Light types
|
||||
#define LIGHT_DIRECTIONAL 0
|
||||
#define LIGHT_POINT 1
|
||||
#define LIGHT_SPOT 2
|
||||
|
||||
// Structures
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
|
||||
// G-Buffer inputs
|
||||
layout(binding = 0, rgba32f) uniform readonly image2D g_position;
|
||||
layout(binding = 1, rgba32f) uniform readonly image2D g_normal;
|
||||
layout(binding = 2, rgba8) uniform readonly image2D g_albedo;
|
||||
|
||||
// New: material params + material id
|
||||
layout(binding = 5, rgba32f) uniform readonly image2D g_material;
|
||||
layout(binding = 6, r32ui) uniform readonly uimage2D g_material_id;
|
||||
|
||||
// Output
|
||||
layout(binding = 3, rgba32f) uniform image2D output_image;
|
||||
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
||||
|
||||
struct Material {
|
||||
vec3 albedo;
|
||||
float metallic;
|
||||
|
|
@ -60,31 +69,29 @@ struct ScatterResult {
|
|||
bool scattered;
|
||||
vec3 attenuation;
|
||||
Ray scattered_ray;
|
||||
float pdf;
|
||||
};
|
||||
|
||||
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
|
||||
// G-Buffer inputs
|
||||
layout(binding = 0, rgba32f) uniform readonly image2D g_position;
|
||||
layout(binding = 1, rgba32f) uniform readonly image2D g_normal;
|
||||
layout(binding = 2, rgba8) uniform readonly image2D g_albedo;
|
||||
|
||||
// Output
|
||||
layout(binding = 3, rgba32f) uniform image2D output_image;
|
||||
layout(binding = 4, rgba32f) uniform image2D accumulation_image;
|
||||
|
||||
// Scene data
|
||||
layout(std430, binding = 0) readonly buffer MaterialBuffer {
|
||||
Material materials[];
|
||||
struct BVHNodeGpu {
|
||||
vec4 aabb_min_left_first; // xyz min, w = left_first (uint bits in float)
|
||||
vec4 aabb_max_count; // xyz max, w = count (uint bits in float)
|
||||
};
|
||||
|
||||
layout(std430, binding = 1) readonly buffer LightBuffer {
|
||||
Light lights[];
|
||||
struct TriangleGpu {
|
||||
vec4 v0_material; // xyz v0, w material_id (uint bits in float)
|
||||
vec4 v1;
|
||||
vec4 v2;
|
||||
vec4 n0;
|
||||
vec4 n1;
|
||||
vec4 n2;
|
||||
vec4 uv0_uv1; // xy uv0, zw uv1
|
||||
vec4 uv2; // xy uv2
|
||||
};
|
||||
|
||||
// Uniforms
|
||||
layout(std430, binding = 0) readonly buffer MaterialBuffer { Material materials[]; };
|
||||
layout(std430, binding = 1) readonly buffer LightBuffer { Light lights[]; };
|
||||
layout(std430, binding = 2) readonly buffer BVHNodeBuffer { BVHNodeGpu bvh_nodes[]; };
|
||||
layout(std430, binding = 3) readonly buffer TriangleBuffer { TriangleGpu bvh_tris[]; };
|
||||
|
||||
uniform uint u_frame_count;
|
||||
uniform uint u_samples_per_pixel;
|
||||
uniform uint u_max_depth;
|
||||
|
|
@ -93,25 +100,12 @@ uniform vec3 u_camera_position;
|
|||
uniform mat4 u_inv_view_projection;
|
||||
uniform bool u_enable_accumulation;
|
||||
uniform bool u_use_bvh;
|
||||
uniform uint u_bvh_node_count;
|
||||
|
||||
// ============================================================================
|
||||
// Utility Functions
|
||||
// Utility
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Saturate float value to [0, 1]
|
||||
*/
|
||||
float saturate(float x) {
|
||||
return clamp(x, 0.0, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Saturate vec3 value to [0, 1]
|
||||
*/
|
||||
vec3 saturate(vec3 x) {
|
||||
return clamp(x, vec3(0.0), vec3(1.0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if vector is near zero
|
||||
*/
|
||||
|
|
@ -136,44 +130,28 @@ vec3 refract_vector(vec3 uv, vec3 n, float etai_over_etat) {
|
|||
return r_out_perp + r_out_parallel;
|
||||
}
|
||||
|
||||
uint as_uint(float f) { return floatBitsToUint(f); }
|
||||
float as_float(uint u) { return uintBitsToFloat(u); }
|
||||
|
||||
// ============================================================================
|
||||
// Random Number Generation
|
||||
// RNG (PCG)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief PCG hash function for random number generation
|
||||
*/
|
||||
uint pcg_hash(uint seed) {
|
||||
uint state = seed * 747796405u + 2891336453u;
|
||||
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
||||
return (word >> 22u) ^ word;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random float in [0, 1)
|
||||
*/
|
||||
float random_float(inout uint seed) {
|
||||
seed = pcg_hash(seed);
|
||||
return float(seed) / 4294967296.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random vec2
|
||||
*/
|
||||
vec2 random_vec2(inout uint seed) {
|
||||
return vec2(random_float(seed), random_float(seed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random vec3
|
||||
*/
|
||||
vec3 random_vec3(inout uint seed) {
|
||||
return vec3(random_float(seed), random_float(seed), random_float(seed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random vector in unit sphere
|
||||
*/
|
||||
vec3 random_in_unit_sphere(inout uint seed) {
|
||||
while (true) {
|
||||
vec3 p = 2.0 * random_vec3(seed) - vec3(1.0);
|
||||
|
|
@ -181,504 +159,406 @@ vec3 random_in_unit_sphere(inout uint seed) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random unit vector
|
||||
*/
|
||||
vec3 random_unit_vector(inout uint seed) {
|
||||
return normalize(random_in_unit_sphere(seed));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sampling Functions
|
||||
// Camera ray
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Cosine-weighted hemisphere sampling
|
||||
* @return Sampled direction in local space (z-up)
|
||||
* @brief Generate primary ray in world space
|
||||
*/
|
||||
vec3 cosine_weighted_hemisphere(inout uint seed) {
|
||||
vec2 r = random_vec2(seed);
|
||||
float phi = 2.0 * PI * r.x;
|
||||
float cos_theta = sqrt(r.y);
|
||||
float sin_theta = sqrt(1.0 - r.y);
|
||||
Ray generate_camera_ray(ivec2 pixel_coords, ivec2 image_size, inout uint seed) {
|
||||
vec2 jitter = vec2(random_float(seed), random_float(seed));
|
||||
vec2 uv = (vec2(pixel_coords) + jitter) / vec2(image_size);
|
||||
vec2 ndc = uv * 2.0 - 1.0;
|
||||
|
||||
return vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta);
|
||||
}
|
||||
vec4 p_near = u_inv_view_projection * vec4(ndc, 0.0, 1.0);
|
||||
vec4 p_far = u_inv_view_projection * vec4(ndc, 1.0, 1.0);
|
||||
vec3 near_ws = p_near.xyz / p_near.w;
|
||||
vec3 far_ws = p_far.xyz / p_far.w;
|
||||
|
||||
/**
|
||||
* @brief Build orthonormal basis from normal
|
||||
*/
|
||||
void build_onb(vec3 normal, out vec3 tangent, out vec3 bitangent) {
|
||||
vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
tangent = normalize(cross(up, normal));
|
||||
bitangent = cross(normal, tangent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transform direction from local to world space
|
||||
*/
|
||||
vec3 local_to_world(vec3 local_dir, vec3 normal) {
|
||||
vec3 tangent, bitangent;
|
||||
build_onb(normal, tangent, bitangent);
|
||||
return tangent * local_dir.x + bitangent * local_dir.y + normal * local_dir.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sample GGX distribution for microfacet normal
|
||||
*/
|
||||
vec3 sample_ggx(vec3 normal, float roughness, inout uint seed) {
|
||||
vec2 r = random_vec2(seed);
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
|
||||
float phi = 2.0 * PI * r.x;
|
||||
float cos_theta = sqrt((1.0 - r.y) / (1.0 + (a2 - 1.0) * r.y));
|
||||
float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
|
||||
|
||||
vec3 local_h = vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta);
|
||||
return local_to_world(local_h, normal);
|
||||
Ray r;
|
||||
r.origin = near_ws;
|
||||
r.direction = normalize(far_ws - near_ws);
|
||||
return r;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BRDF Functions
|
||||
// Intersection
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Schlick's approximation for Fresnel reflectance
|
||||
* @brief Ray-AABB intersection
|
||||
*/
|
||||
bool intersect_aabb(Ray ray, vec3 aabb_min, vec3 aabb_max, float t_max) {
|
||||
vec3 inv_d = 1.0 / ray.direction;
|
||||
vec3 t0 = (aabb_min - ray.origin) * inv_d;
|
||||
vec3 t1 = (aabb_max - ray.origin) * inv_d;
|
||||
|
||||
vec3 tmin3 = min(t0, t1);
|
||||
vec3 tmax3 = max(t0, t1);
|
||||
|
||||
float tmin = max(max(tmin3.x, tmin3.y), tmin3.z);
|
||||
float tmax2 = min(min(tmax3.x, tmax3.y), tmax3.z);
|
||||
|
||||
return (tmax2 >= max(tmin, 0.0)) && (tmin <= t_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Moller-Trumbore triangle intersection
|
||||
*/
|
||||
bool intersect_triangle(Ray ray, TriangleGpu tri, inout HitInfo hit) {
|
||||
vec3 v0 = tri.v0_material.xyz;
|
||||
vec3 v1 = tri.v1.xyz;
|
||||
vec3 v2 = tri.v2.xyz;
|
||||
|
||||
vec3 e1 = v1 - v0;
|
||||
vec3 e2 = v2 - v0;
|
||||
vec3 pvec = cross(ray.direction, e2);
|
||||
float det = dot(e1, pvec);
|
||||
|
||||
if (abs(det) < EPSILON) return false;
|
||||
float inv_det = 1.0 / det;
|
||||
|
||||
vec3 tvec = ray.origin - v0;
|
||||
float u = dot(tvec, pvec) * inv_det;
|
||||
if (u < 0.0 || u > 1.0) return false;
|
||||
|
||||
vec3 qvec = cross(tvec, e1);
|
||||
float v = dot(ray.direction, qvec) * inv_det;
|
||||
if (v < 0.0 || u + v > 1.0) return false;
|
||||
|
||||
float t = dot(e2, qvec) * inv_det;
|
||||
if (t < EPSILON || t >= hit.t) return false;
|
||||
|
||||
// Interpolate normal/uv
|
||||
float w = 1.0 - u - v;
|
||||
vec3 n0 = tri.n0.xyz;
|
||||
vec3 n1 = tri.n1.xyz;
|
||||
vec3 n2 = tri.n2.xyz;
|
||||
|
||||
vec2 uv0 = tri.uv0_uv1.xy;
|
||||
vec2 uv1 = tri.uv0_uv1.zw;
|
||||
vec2 uv2 = tri.uv2.xy;
|
||||
|
||||
hit.hit = true;
|
||||
hit.t = t;
|
||||
hit.position = ray.origin + t * ray.direction;
|
||||
hit.normal = normalize(n0 * w + n1 * u + n2 * v);
|
||||
hit.texcoord = uv0 * w + uv1 * u + uv2 * v;
|
||||
hit.material_id = as_uint(tri.v0_material.w);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief BVH traversal (closest hit)
|
||||
*/
|
||||
HitInfo trace_ray_bvh(Ray ray) {
|
||||
HitInfo hit;
|
||||
hit.hit = false;
|
||||
hit.t = MAX_FLOAT;
|
||||
|
||||
if (!u_use_bvh || u_bvh_node_count == 0u) {
|
||||
return hit;
|
||||
}
|
||||
|
||||
// Small fixed stack
|
||||
uint stack[64];
|
||||
int sp = 0;
|
||||
stack[sp++] = 0u;
|
||||
|
||||
while (sp > 0) {
|
||||
uint node_idx = stack[--sp];
|
||||
if (node_idx >= u_bvh_node_count) continue;
|
||||
|
||||
BVHNodeGpu node = bvh_nodes[node_idx];
|
||||
vec3 bmin = node.aabb_min_left_first.xyz;
|
||||
vec3 bmax = node.aabb_max_count.xyz;
|
||||
uint left_first = as_uint(node.aabb_min_left_first.w);
|
||||
uint count = as_uint(node.aabb_max_count.w);
|
||||
|
||||
if (!intersect_aabb(ray, bmin, bmax, hit.t)) continue;
|
||||
|
||||
if (count > 0u) {
|
||||
for (uint i = 0u; i < count; ++i) {
|
||||
TriangleGpu tri = bvh_tris[left_first + i];
|
||||
intersect_triangle(ray, tri, hit);
|
||||
}
|
||||
} else {
|
||||
// Interior: push children
|
||||
uint left = left_first;
|
||||
uint right = left_first + 1u;
|
||||
|
||||
// Depth-first; no sorting (simple)
|
||||
if (sp < 63) stack[sp++] = right;
|
||||
if (sp < 63) stack[sp++] = left;
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Any-hit BVH for shadow ray
|
||||
*/
|
||||
bool trace_any_bvh(Ray ray, float t_max) {
|
||||
if (!u_use_bvh || u_bvh_node_count == 0u) return false;
|
||||
|
||||
uint stack[64];
|
||||
int sp = 0;
|
||||
stack[sp++] = 0u;
|
||||
|
||||
HitInfo hit;
|
||||
hit.hit = false;
|
||||
hit.t = t_max;
|
||||
|
||||
while (sp > 0) {
|
||||
uint node_idx = stack[--sp];
|
||||
if (node_idx >= u_bvh_node_count) continue;
|
||||
|
||||
BVHNodeGpu node = bvh_nodes[node_idx];
|
||||
vec3 bmin = node.aabb_min_left_first.xyz;
|
||||
vec3 bmax = node.aabb_max_count.xyz;
|
||||
uint left_first = as_uint(node.aabb_min_left_first.w);
|
||||
uint count = as_uint(node.aabb_max_count.w);
|
||||
|
||||
if (!intersect_aabb(ray, bmin, bmax, hit.t)) continue;
|
||||
|
||||
if (count > 0u) {
|
||||
for (uint i = 0u; i < count; ++i) {
|
||||
TriangleGpu tri = bvh_tris[left_first + i];
|
||||
if (intersect_triangle(ray, tri, hit)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint left = left_first;
|
||||
uint right = left_first + 1u;
|
||||
if (sp < 63) stack[sp++] = right;
|
||||
if (sp < 63) stack[sp++] = left;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Material + scattering
|
||||
// ============================================================================
|
||||
|
||||
vec3 fresnel_schlick(float cos_theta, vec3 f0) {
|
||||
return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fresnel reflectance for dielectrics
|
||||
*/
|
||||
float fresnel_dielectric(float cos_theta, float ior) {
|
||||
float r0 = (1.0 - ior) / (1.0 + ior);
|
||||
r0 = r0 * r0;
|
||||
return r0 + (1.0 - r0) * pow(1.0 - cos_theta, 5.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief GGX normal distribution function
|
||||
*/
|
||||
float distribution_ggx(vec3 N, vec3 H, float roughness) {
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
float NdotH2 = NdotH * NdotH;
|
||||
|
||||
float nom = a2;
|
||||
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
||||
denom = PI * denom * denom;
|
||||
|
||||
return nom / max(denom, EPSILON);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Smith's geometry function for GGX
|
||||
*/
|
||||
float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) {
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float r = roughness + 1.0;
|
||||
float k = (r * r) / 8.0;
|
||||
|
||||
float ggx1 = NdotV / (NdotV * (1.0 - k) + k);
|
||||
float ggx2 = NdotL / (NdotL * (1.0 - k) + k);
|
||||
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Material Scattering
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Scatter ray for diffuse material
|
||||
*/
|
||||
ScatterResult scatter_diffuse(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
ScatterResult result;
|
||||
result.scattered = true;
|
||||
result.attenuation = mat.albedo;
|
||||
ScatterResult r;
|
||||
r.scattered = true;
|
||||
r.attenuation = mat.albedo;
|
||||
|
||||
// Cosine-weighted hemisphere sampling
|
||||
vec3 local_dir = cosine_weighted_hemisphere(seed);
|
||||
vec3 scatter_direction = local_to_world(local_dir, hit.normal);
|
||||
vec3 dir = hit.normal + random_unit_vector(seed);
|
||||
if (near_zero(dir)) dir = hit.normal;
|
||||
|
||||
// Prevent degenerate scatter direction
|
||||
if (near_zero(scatter_direction)) {
|
||||
scatter_direction = hit.normal;
|
||||
}
|
||||
|
||||
result.scattered_ray.origin = hit.position + hit.normal * EPSILON;
|
||||
result.scattered_ray.direction = normalize(scatter_direction);
|
||||
result.pdf = max(dot(hit.normal, result.scattered_ray.direction), 0.0) * INV_PI;
|
||||
|
||||
return result;
|
||||
r.scattered_ray.origin = hit.position + hit.normal * EPSILON;
|
||||
r.scattered_ray.direction = normalize(dir);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Scatter ray for metal material
|
||||
*/
|
||||
ScatterResult scatter_metal(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
ScatterResult result;
|
||||
ScatterResult r;
|
||||
|
||||
vec3 reflected = reflect_vector(normalize(ray_in.direction), hit.normal);
|
||||
|
||||
// Add roughness perturbation
|
||||
vec3 fuzz = mat.roughness * random_in_unit_sphere(seed);
|
||||
vec3 scatter_direction = reflected + fuzz;
|
||||
vec3 dir = reflected + fuzz;
|
||||
|
||||
result.scattered = dot(scatter_direction, hit.normal) > 0.0;
|
||||
|
||||
if (result.scattered) {
|
||||
result.scattered_ray.origin = hit.position + hit.normal * EPSILON;
|
||||
result.scattered_ray.direction = normalize(scatter_direction);
|
||||
|
||||
vec3 V = -normalize(ray_in.direction);
|
||||
vec3 H = normalize(V + result.scattered_ray.direction);
|
||||
vec3 F0 = mat.albedo;
|
||||
|
||||
result.attenuation = fresnel_schlick(max(dot(H, V), 0.0), F0);
|
||||
result.pdf = 1.0; // Delta distribution approximation
|
||||
} else {
|
||||
result.attenuation = vec3(0.0);
|
||||
result.pdf = 0.0;
|
||||
}
|
||||
|
||||
return result;
|
||||
r.scattered = dot(dir, hit.normal) > 0.0;
|
||||
r.attenuation = mat.albedo;
|
||||
r.scattered_ray.origin = hit.position + hit.normal * EPSILON;
|
||||
r.scattered_ray.direction = normalize(dir);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Scatter ray for dielectric material (glass)
|
||||
*/
|
||||
ScatterResult scatter_dielectric(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
ScatterResult result;
|
||||
result.scattered = true;
|
||||
result.attenuation = vec3(1.0);
|
||||
ScatterResult r;
|
||||
r.scattered = true;
|
||||
r.attenuation = vec3(1.0);
|
||||
|
||||
float refraction_ratio = dot(ray_in.direction, hit.normal) < 0.0 ?
|
||||
(1.0 / mat.ior) : mat.ior;
|
||||
|
||||
vec3 unit_direction = normalize(ray_in.direction);
|
||||
float cos_theta = min(dot(-unit_direction, hit.normal), 1.0);
|
||||
float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
|
||||
vec3 unit_dir = normalize(ray_in.direction);
|
||||
float cos_theta = min(dot(-unit_dir, hit.normal), 1.0);
|
||||
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
|
||||
|
||||
float refraction_ratio = dot(unit_dir, hit.normal) < 0.0 ? (1.0 / mat.ior) : mat.ior;
|
||||
bool cannot_refract = refraction_ratio * sin_theta > 1.0;
|
||||
float reflectance = fresnel_dielectric(cos_theta, refraction_ratio);
|
||||
float reflect_prob = fresnel_dielectric(cos_theta, refraction_ratio);
|
||||
|
||||
vec3 direction;
|
||||
if (cannot_refract || reflectance > random_float(seed)) {
|
||||
direction = reflect_vector(unit_direction, hit.normal);
|
||||
vec3 dir;
|
||||
if (cannot_refract || random_float(seed) < reflect_prob) {
|
||||
dir = reflect_vector(unit_dir, hit.normal);
|
||||
} else {
|
||||
direction = refract_vector(unit_direction, hit.normal, refraction_ratio);
|
||||
dir = refract_vector(unit_dir, hit.normal, refraction_ratio);
|
||||
}
|
||||
|
||||
result.scattered_ray.origin = hit.position + direction * EPSILON;
|
||||
result.scattered_ray.direction = normalize(direction);
|
||||
result.pdf = 1.0; // Delta distribution
|
||||
|
||||
return result;
|
||||
r.scattered_ray.origin = hit.position + dir * EPSILON;
|
||||
r.scattered_ray.direction = normalize(dir);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Scatter ray based on material type
|
||||
*/
|
||||
ScatterResult scatter_ray(Ray ray_in, HitInfo hit, Material mat, inout uint seed) {
|
||||
if (mat.type == MATERIAL_DIFFUSE) {
|
||||
return scatter_diffuse(ray_in, hit, mat, seed);
|
||||
} else if (mat.type == MATERIAL_METAL) {
|
||||
return scatter_metal(ray_in, hit, mat, seed);
|
||||
} else if (mat.type == MATERIAL_DIELECTRIC) {
|
||||
return scatter_dielectric(ray_in, hit, mat, seed);
|
||||
} else {
|
||||
// Emissive material doesn't scatter
|
||||
ScatterResult result;
|
||||
result.scattered = false;
|
||||
result.attenuation = vec3(0.0);
|
||||
result.pdf = 0.0;
|
||||
return result;
|
||||
}
|
||||
if (mat.type == MATERIAL_DIFFUSE) return scatter_diffuse(ray_in, hit, mat, seed);
|
||||
if (mat.type == MATERIAL_METAL) return scatter_metal(ray_in, hit, mat, seed);
|
||||
if (mat.type == MATERIAL_DIELECTRIC) return scatter_dielectric(ray_in, hit, mat, seed);
|
||||
|
||||
ScatterResult r;
|
||||
r.scattered = false;
|
||||
r.attenuation = vec3(0.0);
|
||||
return r;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Scene Intersection (G-Buffer based)
|
||||
// Direct lighting (with shadow ray)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Trace ray against G-Buffer (single bounce only)
|
||||
* @note This is a simplified version - full path tracing needs scene geometry
|
||||
*/
|
||||
HitInfo trace_ray_gbuffer(Ray ray, ivec2 pixel_coords) {
|
||||
HitInfo hit;
|
||||
|
||||
// Read from G-Buffer at current pixel
|
||||
vec4 position_data = imageLoad(g_position, pixel_coords);
|
||||
vec4 normal_data = imageLoad(g_normal, pixel_coords);
|
||||
vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
|
||||
|
||||
if (position_data.w > 0.5) {
|
||||
hit.hit = true;
|
||||
hit.position = position_data.xyz;
|
||||
hit.normal = normalize(normal_data.xyz);
|
||||
hit.material_id = uint(albedo_data.a * 255.0 + 0.5);
|
||||
hit.t = length(hit.position - ray.origin);
|
||||
} else {
|
||||
hit.hit = false;
|
||||
hit.t = MAX_FLOAT;
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Path Tracing Core
|
||||
// ============================================================================
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sample direct lighting from light sources
|
||||
*/
|
||||
vec3 sample_direct_lighting(vec3 position, vec3 normal, Material mat, inout uint seed) {
|
||||
vec3 eval_direct_lighting(HitInfo hit, Material mat, inout uint seed) {
|
||||
if (u_light_count == 0u) return vec3(0.0);
|
||||
|
||||
vec3 direct_light = vec3(0.0);
|
||||
|
||||
// Sample one random light (could be improved with MIS)
|
||||
// sample one light
|
||||
uint light_idx = uint(random_float(seed) * float(u_light_count)) % u_light_count;
|
||||
Light light = lights[light_idx];
|
||||
|
||||
vec3 light_dir;
|
||||
float light_distance;
|
||||
float pdf_light = 1.0 / float(u_light_count);
|
||||
vec3 L;
|
||||
float dist = MAX_FLOAT;
|
||||
vec3 radiance = vec3(0.0);
|
||||
|
||||
if (light.type == LIGHT_POINT) {
|
||||
vec3 to_light = light.position - position;
|
||||
light_distance = length(to_light);
|
||||
light_dir = to_light / light_distance;
|
||||
vec3 to_light = light.position - hit.position;
|
||||
dist = length(to_light);
|
||||
if (dist > light.range) return vec3(0.0);
|
||||
L = to_light / dist;
|
||||
|
||||
if (light_distance > light.range) return vec3(0.0);
|
||||
|
||||
float NdotL = max(dot(normal, light_dir), 0.0);
|
||||
if (NdotL > 0.0) {
|
||||
float attenuation = 1.0 / max(light_distance * light_distance, 0.01);
|
||||
vec3 radiance = light.color * light.intensity * attenuation;
|
||||
|
||||
// Simple BRDF evaluation (diffuse)
|
||||
direct_light = mat.albedo * INV_PI * radiance * NdotL / pdf_light;
|
||||
}
|
||||
float atten = 1.0 / max(dist * dist, 0.01);
|
||||
radiance = light.color * light.intensity * atten;
|
||||
} else if (light.type == LIGHT_DIRECTIONAL) {
|
||||
light_dir = normalize(-light.direction);
|
||||
float NdotL = max(dot(normal, light_dir), 0.0);
|
||||
|
||||
if (NdotL > 0.0) {
|
||||
vec3 radiance = light.color * light.intensity;
|
||||
direct_light = mat.albedo * INV_PI * radiance * NdotL / pdf_light;
|
||||
}
|
||||
L = normalize(-light.direction);
|
||||
radiance = light.color * light.intensity;
|
||||
} else {
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
return direct_light;
|
||||
float n_dot_l = max(dot(hit.normal, L), 0.0);
|
||||
if (n_dot_l <= 0.0) return vec3(0.0);
|
||||
|
||||
// shadow ray
|
||||
Ray shadow_ray;
|
||||
shadow_ray.origin = hit.position + hit.normal * EPSILON;
|
||||
shadow_ray.direction = L;
|
||||
|
||||
float t_max = (light.type == LIGHT_POINT) ? (dist - EPSILON) : MAX_FLOAT;
|
||||
if (trace_any_bvh(shadow_ray, t_max)) {
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
float pdf_light = 1.0 / float(u_light_count);
|
||||
vec3 brdf = mat.albedo * INV_PI; // diffuse direct only (simple)
|
||||
return brdf * radiance * n_dot_l / max(pdf_light, EPSILON);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Trace path and accumulate radiance
|
||||
*/
|
||||
vec3 trace_path(Ray initial_ray, ivec2 pixel_coords, inout uint seed) {
|
||||
// ============================================================================
|
||||
// Path tracing
|
||||
// ============================================================================
|
||||
|
||||
Material fetch_material(uint material_id) {
|
||||
uint cnt = uint(materials.length());
|
||||
if (material_id < cnt) return materials[material_id];
|
||||
|
||||
Material m;
|
||||
m.albedo = vec3(0.5);
|
||||
m.metallic = 0.0;
|
||||
m.emission = vec3(0.0);
|
||||
m.roughness = 0.5;
|
||||
m.type = MATERIAL_DIFFUSE;
|
||||
m.ior = 1.5;
|
||||
return m;
|
||||
}
|
||||
|
||||
vec3 environment_color(vec3 dir) {
|
||||
// simple dark sky
|
||||
return vec3(0.1, 0.1, 0.15);
|
||||
}
|
||||
|
||||
vec3 trace_path(Ray ray, inout uint seed) {
|
||||
vec3 radiance = vec3(0.0);
|
||||
vec3 throughput = vec3(1.0);
|
||||
Ray current_ray = initial_ray;
|
||||
|
||||
uint mat_count = uint(materials.length());
|
||||
|
||||
for (uint depth = 0u; depth < u_max_depth; depth++) {
|
||||
// Trace ray (only first bounce uses G-Buffer)
|
||||
HitInfo hit;
|
||||
if (depth == 0u) {
|
||||
hit = trace_ray_gbuffer(current_ray, pixel_coords);
|
||||
} else {
|
||||
// For subsequent bounces, we can't trace without full scene geometry
|
||||
// This is a limitation of G-Buffer based approach
|
||||
// In a full path tracer, you'd trace against the actual scene here
|
||||
hit.hit = false;
|
||||
}
|
||||
|
||||
for (uint depth = 0u; depth < u_max_depth; ++depth) {
|
||||
HitInfo hit = trace_ray_bvh(ray);
|
||||
if (!hit.hit) {
|
||||
// Hit sky/background
|
||||
vec3 sky_color = vec3(0.1, 0.1, 0.15);
|
||||
radiance += throughput * sky_color;
|
||||
radiance += throughput * environment_color(ray.direction);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get material
|
||||
Material mat;
|
||||
if (hit.material_id < mat_count) {
|
||||
mat = materials[hit.material_id];
|
||||
} else {
|
||||
// Fallback material
|
||||
mat.albedo = vec3(0.5);
|
||||
mat.metallic = 0.0;
|
||||
mat.roughness = 0.5;
|
||||
mat.emission = vec3(0.0);
|
||||
mat.type = MATERIAL_DIFFUSE;
|
||||
mat.ior = 1.5;
|
||||
}
|
||||
Material mat = fetch_material(hit.material_id);
|
||||
|
||||
// Add emission
|
||||
// emission
|
||||
radiance += throughput * mat.emission;
|
||||
|
||||
// Sample direct lighting (only for diffuse surfaces)
|
||||
if (mat.type == MATERIAL_DIFFUSE && depth == 0u) {
|
||||
radiance += throughput * sample_direct_lighting(hit.position, hit.normal, mat, seed);
|
||||
// direct light (only for diffuse to keep simple)
|
||||
if (mat.type == MATERIAL_DIFFUSE) {
|
||||
radiance += throughput * eval_direct_lighting(hit, mat, seed);
|
||||
}
|
||||
|
||||
// Scatter ray
|
||||
ScatterResult scatter = scatter_ray(current_ray, hit, mat, seed);
|
||||
ScatterResult sc = scatter_ray(ray, hit, mat, seed);
|
||||
if (!sc.scattered) break;
|
||||
|
||||
if (!scatter.scattered || scatter.pdf < EPSILON) {
|
||||
break;
|
||||
}
|
||||
throughput *= sc.attenuation;
|
||||
|
||||
// Update throughput
|
||||
throughput *= scatter.attenuation;
|
||||
|
||||
// Russian roulette path termination
|
||||
// RR
|
||||
if (depth > 3u) {
|
||||
float rr_probability = max(throughput.r, max(throughput.g, throughput.b));
|
||||
if (rr_probability < RR_THRESHOLD || random_float(seed) > rr_probability) {
|
||||
break;
|
||||
}
|
||||
throughput /= rr_probability;
|
||||
float p = max(throughput.r, max(throughput.g, throughput.b));
|
||||
p = clamp(p, 0.0, 0.95);
|
||||
if (p < RR_THRESHOLD || random_float(seed) > p) break;
|
||||
throughput /= p;
|
||||
}
|
||||
|
||||
// Continue with scattered ray
|
||||
current_ray = scatter.scattered_ray;
|
||||
ray = sc.scattered_ray;
|
||||
|
||||
// Safety check for throughput
|
||||
if (all(lessThan(throughput, vec3(EPSILON)))) {
|
||||
break;
|
||||
}
|
||||
if (all(lessThan(throughput, vec3(EPSILON)))) break;
|
||||
}
|
||||
|
||||
return radiance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enhanced direct lighting with G-Buffer
|
||||
*/
|
||||
vec3 render_direct_lighting(ivec2 pixel_coords, inout uint seed) {
|
||||
// Read G-Buffer
|
||||
vec4 position_data = imageLoad(g_position, pixel_coords);
|
||||
vec4 normal_data = imageLoad(g_normal, pixel_coords);
|
||||
vec4 albedo_data = imageLoad(g_albedo, pixel_coords);
|
||||
|
||||
if (position_data.w < 0.5) {
|
||||
return vec3(0.1, 0.1, 0.15); // Sky
|
||||
}
|
||||
|
||||
vec3 position = position_data.xyz;
|
||||
vec3 normal = normalize(normal_data.xyz);
|
||||
uint material_id = uint(albedo_data.a * 255.0 + 0.5);
|
||||
|
||||
// Get material
|
||||
Material mat;
|
||||
uint mat_count = uint(materials.length());
|
||||
|
||||
if (material_id < mat_count) {
|
||||
mat = materials[material_id];
|
||||
} else {
|
||||
// Fallback: use G-Buffer albedo
|
||||
mat.albedo = albedo_data.rgb;
|
||||
mat.metallic = 0.0;
|
||||
mat.roughness = 0.5;
|
||||
mat.emission = vec3(0.0);
|
||||
mat.type = MATERIAL_DIFFUSE;
|
||||
mat.ior = 1.5;
|
||||
}
|
||||
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
// Emission
|
||||
color += mat.emission;
|
||||
|
||||
// View direction
|
||||
vec3 view_dir = normalize(u_camera_position - position);
|
||||
|
||||
// Direct lighting from all lights
|
||||
for (uint i = 0u; i < u_light_count; i++) {
|
||||
Light light = lights[i];
|
||||
vec3 light_dir;
|
||||
float light_distance;
|
||||
float attenuation = 1.0;
|
||||
|
||||
if (light.type == LIGHT_POINT) {
|
||||
vec3 to_light = light.position - position;
|
||||
light_distance = length(to_light);
|
||||
light_dir = to_light / light_distance;
|
||||
attenuation = 1.0 / max(light_distance * light_distance, 0.01);
|
||||
|
||||
if (light_distance > light.range) continue;
|
||||
} else if (light.type == LIGHT_DIRECTIONAL) {
|
||||
light_dir = normalize(-light.direction);
|
||||
light_distance = MAX_FLOAT;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
float NdotL = max(dot(normal, light_dir), 0.0);
|
||||
|
||||
if (NdotL > 0.0) {
|
||||
vec3 H = normalize(view_dir + light_dir);
|
||||
float NdotV = max(dot(normal, view_dir), 0.0);
|
||||
float NdotH = max(dot(normal, H), 0.0);
|
||||
float HdotV = max(dot(H, view_dir), 0.0);
|
||||
|
||||
// Cook-Torrance BRDF
|
||||
vec3 F0 = mix(vec3(0.04), mat.albedo, mat.metallic);
|
||||
vec3 F = fresnel_schlick(HdotV, F0);
|
||||
float D = distribution_ggx(normal, H, max(mat.roughness, 0.04));
|
||||
float G = geometry_smith(normal, view_dir, light_dir, mat.roughness);
|
||||
|
||||
vec3 numerator = D * G * F;
|
||||
float denominator = 4.0 * NdotV * NdotL + EPSILON;
|
||||
vec3 specular = numerator / denominator;
|
||||
|
||||
vec3 kS = F;
|
||||
vec3 kD = (vec3(1.0) - kS) * (1.0 - mat.metallic);
|
||||
|
||||
vec3 radiance = light.color * light.intensity * attenuation;
|
||||
color += (kD * mat.albedo * INV_PI + specular) * radiance * NdotL;
|
||||
}
|
||||
}
|
||||
|
||||
// Ambient occlusion approximation
|
||||
color += mat.albedo * 0.03;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 image_size = imageSize(output_image);
|
||||
if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) return;
|
||||
|
||||
if (pixel_coords.x >= image_size.x || pixel_coords.y >= image_size.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize random seed
|
||||
uint base_seed = uint(pixel_coords.x) + uint(pixel_coords.y) * uint(image_size.x);
|
||||
uint seed = base_seed + u_frame_count * 719393u;
|
||||
|
||||
vec3 color = render_direct_lighting(pixel_coords, seed);
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
// Multi-sample
|
||||
uint spp = max(u_samples_per_pixel, 1u);
|
||||
for (uint s = 0u; s < spp; ++s) {
|
||||
Ray cam_ray = generate_camera_ray(pixel_coords, image_size, seed);
|
||||
color += trace_path(cam_ray, seed);
|
||||
}
|
||||
color /= float(spp);
|
||||
|
||||
// Clamp
|
||||
color = clamp(color, vec3(0.0), vec3(10.0));
|
||||
|
||||
// Accumulation
|
||||
if (u_enable_accumulation && u_frame_count > 0u) {
|
||||
vec3 accumulated = imageLoad(accumulation_image, pixel_coords).rgb;
|
||||
float weight = 1.0 / float(u_frame_count + 1u);
|
||||
color = mix(accumulated, color, weight);
|
||||
float w = 1.0 / float(u_frame_count + 1u);
|
||||
color = mix(accumulated, color, w);
|
||||
}
|
||||
|
||||
imageStore(accumulation_image, pixel_coords, vec4(color, 1.0));
|
||||
|
|
|
|||
|
|
@ -261,24 +261,53 @@ bool BVH::upload_to_gpu(Buffer& node_buffer, Buffer& triangle_buffer) {
|
|||
// Reorder triangles according to BVH layout
|
||||
std::vector<Triangle> ordered_triangles;
|
||||
ordered_triangles.reserve(triangles_.size());
|
||||
|
||||
for (uint idx : triangle_indices_) {
|
||||
ordered_triangles.push_back(triangles_[idx]);
|
||||
}
|
||||
|
||||
// Upload nodes
|
||||
// Pack nodes to GPU layout
|
||||
std::vector<BVHNodeGpu> node_gpu;
|
||||
node_gpu.resize(nodes_.size());
|
||||
for (size_t i = 0; i < nodes_.size(); ++i) {
|
||||
const BVHNode& n = nodes_[i];
|
||||
BVHNodeGpu g;
|
||||
g.aabb_min_left_first_ = Vec4(n.aabb_min_, glm::uintBitsToFloat(n.left_first_));
|
||||
g.aabb_max_count_ = Vec4(n.aabb_max_, glm::uintBitsToFloat(n.count_));
|
||||
node_gpu[i] = g;
|
||||
}
|
||||
|
||||
// Pack triangles to GPU layout
|
||||
std::vector<TriangleGpu> tri_gpu;
|
||||
tri_gpu.resize(ordered_triangles.size());
|
||||
for (size_t i = 0; i < ordered_triangles.size(); ++i) {
|
||||
const Triangle& t = ordered_triangles[i];
|
||||
|
||||
TriangleGpu g{};
|
||||
g.v0_material_ = Vec4(t.v0_, glm::uintBitsToFloat(t.material_id_));
|
||||
g.v1_ = Vec4(t.v1_, 0.0f);
|
||||
g.v2_ = Vec4(t.v2_, 0.0f);
|
||||
|
||||
g.n0_ = Vec4(t.n0_, 0.0f);
|
||||
g.n1_ = Vec4(t.n1_, 0.0f);
|
||||
g.n2_ = Vec4(t.n2_, 0.0f);
|
||||
|
||||
g.uv0_uv1_ = Vec4(t.uv0_.x, t.uv0_.y, t.uv1_.x, t.uv1_.y);
|
||||
g.uv2_ = Vec4(t.uv2_.x, t.uv2_.y, 0.0f, 0.0f);
|
||||
|
||||
tri_gpu[i] = g;
|
||||
}
|
||||
|
||||
if (!node_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||||
nodes_.size() * sizeof(BVHNode),
|
||||
nodes_.data(),
|
||||
node_gpu.size() * sizeof(BVHNodeGpu),
|
||||
node_gpu.data(),
|
||||
BufferUsage::STATIC_DRAW)) {
|
||||
Logger::error("Failed to upload BVH nodes to GPU");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload triangles
|
||||
if (!triangle_buffer.create(BufferType::SHADER_STORAGE_BUFFER,
|
||||
ordered_triangles.size() * sizeof(Triangle),
|
||||
ordered_triangles.data(),
|
||||
tri_gpu.size() * sizeof(TriangleGpu),
|
||||
tri_gpu.data(),
|
||||
BufferUsage::STATIC_DRAW)) {
|
||||
Logger::error("Failed to upload BVH triangles to GPU");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -25,24 +25,30 @@ bool GBuffer::initialize() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Create framebuffer
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
|
||||
// Create G-Buffer textures
|
||||
textures_[GBUFFER_POSITION] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||
textures_[GBUFFER_NORMAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||
textures_[GBUFFER_ALBEDO] = create_texture_(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
|
||||
// Attach textures to framebuffer
|
||||
// New: material params (metallic, roughness, ior, type)
|
||||
textures_[GBUFFER_MATERIAL] = create_texture_(GL_RGBA32F, GL_RGBA, GL_FLOAT);
|
||||
|
||||
// New: material id (integer)
|
||||
textures_[GBUFFER_MATERIAL_ID] = create_texture_(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||||
GL_TEXTURE_2D, textures_[GBUFFER_POSITION], 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
||||
GL_TEXTURE_2D, textures_[GBUFFER_NORMAL], 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
|
||||
GL_TEXTURE_2D, textures_[GBUFFER_ALBEDO], 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL,
|
||||
GL_TEXTURE_2D, textures_[GBUFFER_MATERIAL], 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL_ID,
|
||||
GL_TEXTURE_2D, textures_[GBUFFER_MATERIAL_ID], 0);
|
||||
|
||||
// Create depth texture
|
||||
glGenTextures(1, &depth_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width_, height_, 0,
|
||||
|
|
@ -52,15 +58,15 @@ bool GBuffer::initialize() {
|
|||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, depth_texture_, 0);
|
||||
|
||||
// Set draw buffers
|
||||
GLenum draw_buffers[GBUFFER_COUNT] = {
|
||||
GL_COLOR_ATTACHMENT0 + GBUFFER_POSITION,
|
||||
GL_COLOR_ATTACHMENT0 + GBUFFER_NORMAL,
|
||||
GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO
|
||||
GL_COLOR_ATTACHMENT0 + GBUFFER_ALBEDO,
|
||||
GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL,
|
||||
GL_COLOR_ATTACHMENT0 + GBUFFER_MATERIAL_ID
|
||||
};
|
||||
glDrawBuffers(GBUFFER_COUNT, draw_buffers);
|
||||
|
||||
// Check framebuffer completeness
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
Logger::error("GBuffer framebuffer is not complete");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
|
@ -98,6 +104,18 @@ void GBuffer::release() {
|
|||
Logger::info("GBuffer released");
|
||||
}
|
||||
|
||||
TextureHandle GBuffer::create_texture_(uint internal_format, uint format, uint type) {
|
||||
TextureHandle texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_, height_, 0, format, type, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void GBuffer::render(const Scene& scene, const Shader& shader) {
|
||||
if (!initialized_) {
|
||||
Logger::error("GBuffer not initialized");
|
||||
|
|
@ -150,6 +168,9 @@ void GBuffer::render(const Scene& scene, const Shader& shader) {
|
|||
shader.set_float("u_metallic", material->get_metallic());
|
||||
shader.set_float("u_roughness", material->get_roughness());
|
||||
shader.set_uint("u_material_id", material_id);
|
||||
shader.set_float("u_ior", material->get_ior());
|
||||
shader.set_vec3("u_emission", material->get_emission());
|
||||
shader.set_uint("u_material_type", static_cast<uint>(material->get_type()));
|
||||
|
||||
// Bind textures
|
||||
auto albedo_tex = material->get_albedo_texture();
|
||||
|
|
@ -206,16 +227,4 @@ void GBuffer::get_dimensions(uint& width, uint& height) const {
|
|||
height = height_;
|
||||
}
|
||||
|
||||
TextureHandle GBuffer::create_texture_(uint internal_format, uint format, uint type) {
|
||||
TextureHandle texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_, height_, 0, format, type, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
}
|
||||
|
||||
} // namespace are
|
||||
|
|
|
|||
|
|
@ -306,6 +306,9 @@ void RayTracer::bind_gbuffer_(const GBuffer& gbuffer) {
|
|||
glBindImageTexture(0, gbuffer.get_texture(GBUFFER_POSITION), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||||
glBindImageTexture(1, gbuffer.get_texture(GBUFFER_NORMAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||||
glBindImageTexture(2, gbuffer.get_texture(GBUFFER_ALBEDO), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
|
||||
|
||||
glBindImageTexture(5, gbuffer.get_texture(GBUFFER_MATERIAL), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
|
||||
glBindImageTexture(6, gbuffer.get_texture(GBUFFER_MATERIAL_ID), 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
|
||||
}
|
||||
|
||||
void RayTracer::set_compute_shader(const Shader& shader) {
|
||||
|
|
|
|||
|
|
@ -105,13 +105,6 @@ bool ShaderManager::load_builtin_shaders_() {
|
|||
}
|
||||
shader_cache_["gbuffer"] = gbuffer_shader_;
|
||||
|
||||
// Load ray tracing compute shader
|
||||
if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) {
|
||||
Logger::error("Failed to load ray tracing shader");
|
||||
return false;
|
||||
}
|
||||
shader_cache_["raytracing"] = raytracing_shader_;
|
||||
|
||||
// Load ray tracing compute shader
|
||||
Logger::info("Loading ray tracing compute shader...");
|
||||
if (!raytracing_shader_.load_compute("shaders/raytracing.comp")) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
|
||||
# query.sh - 遍历指定文件夹中的 .h 文件并生成 all_headers.md
|
||||
|
||||
# 检查是否提供了目录参数
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "用法: $0 <目标文件夹路径>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_DIR="$1"
|
||||
|
||||
# 检查提供的路径是否为一个存在的目录
|
||||
if [ ! -d "$TARGET_DIR" ]; then
|
||||
echo "错误: 目录 '$TARGET_DIR' 不存在。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 输出文件
|
||||
OUTPUT_FILE="all_files.md"
|
||||
|
||||
# 清空或创建输出文件
|
||||
> "$OUTPUT_FILE"
|
||||
|
||||
echo "正在扫描目录: $TARGET_DIR"
|
||||
# 使用 find 命令查找所有 .h 文件
|
||||
H_FILES=$(find "$TARGET_DIR" -type f -name "*.cpp")
|
||||
|
||||
# 检查是否找到了 .h 文件
|
||||
if [ -z "$H_FILES" ]; then
|
||||
echo "在目录 '$TARGET_DIR' 及其子目录中未找到任何 .h 文件。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 遍历找到的每个 .h 文件
|
||||
for header_file in $H_FILES; do
|
||||
# 获取相对于脚本执行位置的相对路径
|
||||
RELATIVE_PATH=$(realpath --relative-to=. "$header_file")
|
||||
|
||||
# 写入分隔符和文件名
|
||||
{
|
||||
echo "### 文件:$RELATIVE_PATH"
|
||||
echo ""
|
||||
echo '```cpp'
|
||||
cat "$header_file"
|
||||
echo '```'
|
||||
echo "" # 添加一个空行,使文件之间有分隔
|
||||
} >> "$OUTPUT_FILE"
|
||||
|
||||
echo "已处理: $RELATIVE_PATH"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "处理完成!所有头文件内容已合并到 $OUTPUT_FILE 中。"
|
||||
Loading…
Reference in New Issue