Class: Doom::Render::RayTracingRenderer
- Inherits:
-
HardwareRenderer
- Object
- Renderer
- HardwareRenderer
- Doom::Render::RayTracingRenderer
- Defined in:
- lib/doom/render/ray_tracing_renderer.rb
Overview
GPU ray tracer hosted in Gosu's OpenGL context. World triangles are kept in a floating-point data texture and intersected by a fragment shader; the old hardware renderer is inherited only for texture/sprite/UI glue.
Constant Summary collapse
- DATA_WIDTH =
1024- NODE_DATA_WIDTH =
1024- TEXELS_PER_TRIANGLE =
7- TEXELS_PER_NODE =
3- BVH_LEAF_SIZE =
8- MAX_RAY_LIGHTS =
8- SHADOW_SAMPLES =
Soft shadows: each shadow test fires this many rays at jittered points on a small light disc (SHADOW_SOFTNESS map units across) and averages them, so edges get a penumbra instead of a hard cut. More samples = smoother but costs a BVH ray each; radius widens the penumbra.
5- SHADOW_SOFTNESS =
12.0- AMBIENT_LEVEL =
Atmosphere: how much the sector light level fills unlit surfaces, and how bright the sky reads. Both kept low so shadows stay deep and the flashlight carries the scene -- lower is darker/moodier, higher is flatter.
0.18
- SKY_BRIGHTNESS =
was 0.32
0.28
- FOG_DENSITY =
Distance fog gives depth: without it a bright sector reads evenly lit all the way down a corridor. Higher density swallows distance sooner; FOG_MAX is how completely far surfaces fade into the dark fog colour.
0.0011
- FOG_MAX =
was 0.00075
0.94
- RAY_WIDTH =
was 0.82
640- RAY_HEIGHT =
480- MAX_TRIANGLES =
4096- ATLAS_SIZE =
2048- VERTEX_SHADER =
"#version 120\nvarying vec2 screen_uv;\nvoid main() {\n screen_uv = gl_MultiTexCoord0.xy;\n gl_Position = gl_Vertex;\n}\n"- FRAGMENT_SHADER =
"#version 120\nvarying vec2 screen_uv;\nuniform sampler2D triangle_data;\nuniform sampler2D bvh_data;\nuniform sampler2D material_atlas;\nuniform sampler2D sky_texture;\nuniform float data_height;\nuniform float bvh_height;\nuniform int node_count;\nuniform vec3 camera_position;\nuniform vec3 camera_forward;\nuniform vec3 camera_right;\nuniform vec3 camera_up;\nuniform float aspect_ratio;\nuniform int light_count;\nuniform vec4 light_positions[\#{MAX_RAY_LIGHTS}];\nuniform vec4 light_colors[\#{MAX_RAY_LIGHTS}];\nuniform int fog_enabled;\nuniform int flashlight_enabled;\nuniform int bounces_enabled;\n\nvec4 datum(float index) {\n float x = mod(index, \#{DATA_WIDTH}.0);\n float y = floor(index / \#{DATA_WIDTH}.0);\n return texture2D(triangle_data,\n vec2((x + 0.5) / \#{DATA_WIDTH}.0, (y + 0.5) / data_height));\n}\n\nvec4 node_datum(float index) {\n float x = mod(index, \#{NODE_DATA_WIDTH}.0);\n float y = floor(index / \#{NODE_DATA_WIDTH}.0);\n return texture2D(bvh_data,\n vec2((x + 0.5) / \#{NODE_DATA_WIDTH}.0, (y + 0.5) / bvh_height));\n}\n\nbool intersect_box(vec3 origin, vec3 inverse_direction, vec3 minimum,\n vec3 maximum, float distance_limit) {\n vec3 near_values = (minimum - origin) * inverse_direction;\n vec3 far_values = (maximum - origin) * inverse_direction;\n vec3 low = min(near_values, far_values);\n vec3 high = max(near_values, far_values);\n float near_distance = max(max(low.x, low.y), max(low.z, 0.0));\n float far_distance = min(min(high.x, high.y), high.z);\n return near_distance <= far_distance && near_distance < distance_limit;\n}\n\nbool intersect_triangle(vec3 origin, vec3 direction, float base,\n out float distance, out vec2 barycentric) {\n vec3 a = datum(base).xyz;\n vec3 b = datum(base + 1.0).xyz;\n vec3 c = datum(base + 2.0).xyz;\n vec3 edge1 = b - a;\n vec3 edge2 = c - a;\n vec3 p = cross(direction, edge2);\n float determinant = dot(edge1, p);\n if (abs(determinant) < 0.00001) return false;\n float inverse = 1.0 / determinant;\n vec3 t = origin - a;\n float u = dot(t, p) * inverse;\n if (u < 0.0 || u > 1.0) return false;\n vec3 q = cross(t, edge1);\n float v = dot(direction, q) * inverse;\n if (v < 0.0 || u + v > 1.0) return false;\n distance = dot(edge2, q) * inverse;\n barycentric = vec2(u, v);\n return distance > 0.01;\n}\n\nbool accepts_surface(float base, vec2 barycentric) {\n float flags = datum(base + 3.0).w;\n float masked = floor(mod(flags, 4096.0) / 2048.0);\n if (masked < 0.5) return true;\n vec4 uv0_uv1 = datum(base + 4.0);\n vec4 uv2_rect = datum(base + 5.0);\n vec4 rect_size = datum(base + 6.0);\n float w = 1.0 - barycentric.x - barycentric.y;\n vec2 uv = uv0_uv1.xy * w + uv0_uv1.zw * barycentric.x +\n uv2_rect.xy * barycentric.y;\n vec2 wrapped = fract(uv / rect_size.zw);\n vec2 atlas_uv = rect_size.xy + wrapped * uv2_rect.zw;\n return texture2D(material_atlas, atlas_uv).a > 0.5;\n}\n\nbool shadowed(vec3 origin, vec3 direction, float maximum) {\n vec3 inverse_direction = 1.0 / direction;\n int node_index = 0;\n while (node_index < node_count) {\n float node_base = float(node_index * \#{TEXELS_PER_NODE});\n vec4 minimum_escape = node_datum(node_base);\n vec4 maximum_start = node_datum(node_base + 1.0);\n int escape = int(minimum_escape.w + 0.5);\n if (!intersect_box(origin, inverse_direction, minimum_escape.xyz,\n maximum_start.xyz, maximum)) {\n node_index = escape;\n continue;\n }\n if (maximum_start.w >= 0.0) {\n int start = int(maximum_start.w + 0.5);\n int count = int(node_datum(node_base + 2.0).x + 0.5);\n for (int offset = 0; offset < \#{BVH_LEAF_SIZE}; ++offset) {\n if (offset >= count) break;\n float distance;\n vec2 barycentric;\n float triangle_base = float((start + offset) * \#{TEXELS_PER_TRIANGLE});\n if (intersect_triangle(origin, direction, triangle_base,\n distance, barycentric) && distance < maximum &&\n accepts_surface(triangle_base, barycentric)) return true;\n }\n node_index = escape;\n } else {\n node_index += 1;\n }\n }\n return false;\n}\n\n// Average several shadow rays fired at jittered points on a small light\n// disc so shadow edges soften into a penumbra. Returns visibility [0,1].\nfloat shadow_visibility(vec3 origin, vec3 light_position) {\n float lit = 0.0;\n for (int s = 0; s < \#{SHADOW_SAMPLES}; ++s) {\n float angle = float(s) * 2.39996323; // golden-angle spread\n float disc_radius = \#{SHADOW_SOFTNESS} * sqrt((float(s) + 0.5) / float(\#{SHADOW_SAMPLES}));\n vec3 jittered = light_position + camera_right * (cos(angle) * disc_radius) +\n camera_up * (sin(angle) * disc_radius);\n vec3 delta = jittered - origin;\n float light_dist = length(delta);\n if (!shadowed(origin, delta / max(light_dist, 0.001), light_dist - 0.1)) lit += 1.0;\n }\n return lit / float(\#{SHADOW_SAMPLES});\n}\n\nbool trace_scene(vec3 origin, vec3 direction, float distance_limit,\n int ignored_triangle,\n out int hit, out float nearest, out vec2 hit_barycentric) {\n nearest = distance_limit;\n hit = -1;\n hit_barycentric = vec2(0.0);\n vec3 inverse_direction = 1.0 / direction;\n int node_index = 0;\n while (node_index < node_count) {\n float node_base = float(node_index * \#{TEXELS_PER_NODE});\n vec4 minimum_escape = node_datum(node_base);\n vec4 maximum_start = node_datum(node_base + 1.0);\n int escape = int(minimum_escape.w + 0.5);\n if (!intersect_box(origin, inverse_direction, minimum_escape.xyz,\n maximum_start.xyz, nearest)) {\n node_index = escape;\n continue;\n }\n if (maximum_start.w >= 0.0) {\n int start = int(maximum_start.w + 0.5);\n int count = int(node_datum(node_base + 2.0).x + 0.5);\n for (int offset = 0; offset < \#{BVH_LEAF_SIZE}; ++offset) {\n if (offset >= count) break;\n int triangle_index = start + offset;\n if (triangle_index == ignored_triangle) continue;\n float distance;\n vec2 barycentric;\n float triangle_base = float(triangle_index * \#{TEXELS_PER_TRIANGLE});\n if (intersect_triangle(origin, direction, triangle_base,\n distance, barycentric) && distance < nearest &&\n accepts_surface(triangle_base, barycentric)) {\n nearest = distance;\n hit = triangle_index;\n hit_barycentric = barycentric;\n }\n }\n node_index = escape;\n } else {\n node_index += 1;\n }\n }\n return hit >= 0;\n}\n\nvec3 sky_radiance(vec3 direction) {\n float sky_u = atan(direction.y, direction.x) * 2.0 / 3.14159265;\n float sky_v = 0.5 - asin(clamp(direction.z, -1.0, 1.0)) / 3.14159265;\n return texture2D(sky_texture, vec2(sky_u, sky_v * (200.0 / 128.0))).rgb * \#{SKY_BRIGHTNESS};\n}\n\nvec3 secondary_radiance(vec3 origin, vec3 direction, int source_triangle) {\n int secondary_hit;\n float secondary_distance;\n vec2 barycentric;\n if (!trace_scene(origin, direction, 1.0e20, source_triangle, secondary_hit,\n secondary_distance, barycentric))\n return sky_radiance(direction);\n\n float base = float(secondary_hit * \#{TEXELS_PER_TRIANGLE});\n vec4 normal_light = datum(base + 3.0);\n vec4 uv0_uv1 = datum(base + 4.0);\n vec4 uv2_rect = datum(base + 5.0);\n vec4 rect_size = datum(base + 6.0);\n float w = 1.0 - barycentric.x - barycentric.y;\n vec2 uv = uv0_uv1.xy * w + uv0_uv1.zw * barycentric.x + uv2_rect.xy * barycentric.y;\n vec2 atlas_uv = rect_size.xy + fract(uv / rect_size.zw) * uv2_rect.zw;\n vec3 albedo = texture2D(material_atlas, atlas_uv).rgb;\n float emission = floor(mod(normal_light.w, 2048.0) / 1024.0);\n float sector = clamp(mod(normal_light.w, 1024.0) / 255.0, 0.10, 1.0);\n return albedo * (vec3(sector * \#{AMBIENT_LEVEL}) + emission * vec3(0.18, 0.62, 0.12));\n}\n\nfloat noise(vec3 point) {\n return fract(sin(dot(point, vec3(12.9898, 78.233, 37.719))) * 43758.5453);\n}\n\nvec3 diffuse_bounce_direction(vec3 normal, float surface_id) {\n vec3 helper = abs(normal.z) < 0.9 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangent = normalize(cross(helper, normal));\n vec3 bitangent = cross(normal, tangent);\n // Keep the sample fixed to the triangle. Hashing the continuously\n // moving hit point makes indirect light sparkle as the camera moves.\n float angle = noise(vec3(surface_id, surface_id * 0.37, 1.0)) * 6.2831853;\n float radius = 0.65;\n return normalize(normal * sqrt(1.0 - radius * radius) +\n tangent * cos(angle) * radius + bitangent * sin(angle) * radius);\n}\n\nvoid main() {\n vec2 plane = screen_uv * 2.0 - 1.0;\n plane.y /= aspect_ratio;\n vec3 direction = normalize(camera_forward + camera_right * plane.x + camera_up * plane.y);\n float nearest = 1.0e20;\n int hit = -1;\n vec2 hit_barycentric = vec2(0.0);\n vec3 inverse_direction = 1.0 / direction;\n int node_index = 0;\n while (node_index < node_count) {\n float node_base = float(node_index * \#{TEXELS_PER_NODE});\n vec4 minimum_escape = node_datum(node_base);\n vec4 maximum_start = node_datum(node_base + 1.0);\n int escape = int(minimum_escape.w + 0.5);\n if (!intersect_box(camera_position, inverse_direction, minimum_escape.xyz,\n maximum_start.xyz, nearest)) {\n node_index = escape;\n continue;\n }\n if (maximum_start.w >= 0.0) {\n int start = int(maximum_start.w + 0.5);\n int count = int(node_datum(node_base + 2.0).x + 0.5);\n for (int offset = 0; offset < \#{BVH_LEAF_SIZE}; ++offset) {\n if (offset >= count) break;\n int triangle_index = start + offset;\n float distance;\n vec2 barycentric;\n float triangle_base = float(triangle_index * \#{TEXELS_PER_TRIANGLE});\n if (intersect_triangle(camera_position, direction, triangle_base,\n distance, barycentric) && distance < nearest &&\n accepts_surface(triangle_base, barycentric)) {\n nearest = distance;\n hit = triangle_index;\n hit_barycentric = barycentric;\n }\n }\n node_index = escape;\n } else {\n node_index += 1;\n }\n }\n if (hit < 0) {\n // Match HardwareRenderer::draw_sky and Doom's SKY1 density:\n // repeat the 256px panorama four times around the player and map\n // 200 sky texels over the full view height.\n float sky_u = atan(direction.y, direction.x) * 2.0 / 3.14159265;\n float sky_v = (1.0 - screen_uv.y) * (200.0 / 128.0);\n vec3 sky = texture2D(sky_texture, vec2(sky_u, sky_v)).rgb;\n gl_FragColor = vec4(sky * \#{SKY_BRIGHTNESS}, 1.0);\n return;\n }\n float base = float(hit * \#{TEXELS_PER_TRIANGLE});\n vec4 normal_light = datum(base + 3.0);\n vec4 uv0_uv1 = datum(base + 4.0);\n vec4 uv2_rect = datum(base + 5.0);\n vec4 rect_size = datum(base + 6.0);\n float w = 1.0 - hit_barycentric.x - hit_barycentric.y;\n vec2 uv = uv0_uv1.xy * w + uv0_uv1.zw * hit_barycentric.x + uv2_rect.xy * hit_barycentric.y;\n vec2 wrapped = fract(uv / rect_size.zw);\n vec2 atlas_uv = rect_size.xy + wrapped * uv2_rect.zw;\n vec3 albedo = texture2D(material_atlas, atlas_uv).rgb;\n vec3 normal = normalize(normal_light.xyz);\n if (dot(normal, direction) > 0.0) normal = -normal;\n vec3 point = camera_position + direction * nearest;\n float emission = floor(mod(normal_light.w, 2048.0) / 1024.0);\n float sector = clamp(mod(normal_light.w, 1024.0) / 255.0, 0.10, 1.0);\n vec3 ambient = albedo * sector * \#{AMBIENT_LEVEL};\n vec3 direct = vec3(0.0);\n float strongest_score = 0.0;\n float second_score = 0.0;\n vec3 strongest_direct = vec3(0.0);\n vec3 second_direct = vec3(0.0);\n vec3 strongest_direction = vec3(0.0);\n vec3 second_direction = vec3(0.0);\n float strongest_distance = 0.0;\n float second_distance = 0.0;\n for (int light_index = 0; light_index < \#{MAX_RAY_LIGHTS}; ++light_index) {\n if (light_index >= light_count) break;\n vec3 to_light = light_positions[light_index].xyz - point;\n float light_distance = length(to_light);\n vec3 light_direction = to_light / max(light_distance, 0.001);\n float diffuse = max(dot(normal, light_direction), 0.0);\n // A gentler physically-shaped falloff keeps distant visible lamps\n // contributing instead of crossing an apparent hard threshold.\n float attenuation = 1.0 / (1.0 + light_distance * 0.0015 +\n light_distance * light_distance * 0.000004);\n float contribution = diffuse * attenuation;\n if (contribution < 0.003) continue;\n vec3 light_direct = albedo * light_colors[light_index].rgb * contribution * 1.8;\n direct += light_direct;\n float score = contribution * dot(light_colors[light_index].rgb, vec3(0.30, 0.59, 0.11));\n if (score > strongest_score) {\n second_score = strongest_score;\n second_direct = strongest_direct;\n second_direction = strongest_direction;\n second_distance = strongest_distance;\n strongest_score = score;\n strongest_direct = light_direct;\n strongest_direction = light_direction;\n strongest_distance = light_distance;\n } else if (score > second_score) {\n second_score = score;\n second_direct = light_direct;\n second_direction = light_direction;\n second_distance = light_distance;\n }\n }\n // Flashlight beam coverage for this point: wide and feathered so the\n // beam edge is soft, not a hard disc.\n float flashlight_beam = 0.0;\n if (flashlight_enabled != 0) {\n flashlight_beam = smoothstep(0.65, 0.95,\n dot(normalize(point - camera_position), camera_forward));\n }\n\n // All lights illuminate, but only the two dominant contributors launch\n // expensive BVH shadow rays for this surface.\n if (strongest_score > 0.003) {\n float strongest_visibility = shadow_visibility(point + normal * 0.08,\n point + strongest_direction * strongest_distance);\n direct -= strongest_direct * 0.92 * (1.0 - strongest_visibility);\n }\n if (second_score > 0.003) {\n float second_visibility = shadow_visibility(point + normal * 0.08,\n point + second_direction * second_distance);\n direct -= second_direct * 0.92 * (1.0 - second_visibility);\n }\n\n if (flashlight_enabled != 0) {\n // The flashlight sits slightly to the side of and below the eye, not\n // exactly at it. A light co-located with the camera can never cast a\n // visible shadow: the shadow of any caster falls directly behind it,\n // hidden from the eye, and the shadow ray back to the camera retraces\n // the (empty by construction) primary ray. Offsetting it like a\n // handheld lantern throws shadows to the side where they show.\n vec3 flashlight_position = camera_position + camera_right * 14.0 - camera_up * 10.0;\n\n // The beam still points where you look; reuse the wide coverage.\n float cone = flashlight_beam;\n\n // Lighting and shadowing come from the offset lantern position.\n vec3 to_flashlight = flashlight_position - point;\n float flashlight_distance = length(to_flashlight);\n vec3 flashlight_direction = to_flashlight / max(flashlight_distance, 0.001);\n float facing = max(dot(normal, flashlight_direction), 0.0);\n float flashlight_attenuation = 1.0 / (1.0 + flashlight_distance * 0.0015 +\n flashlight_distance * flashlight_distance * 0.000002);\n float flashlight_strength = cone * facing * flashlight_attenuation;\n if (flashlight_strength > 0.004) {\n float flashlight_visible = mix(0.06, 1.0,\n shadow_visibility(point + normal * 0.08, flashlight_position));\n direct += albedo * vec3(1.0, 0.88, 0.68) * flashlight_strength *\n flashlight_visible * 2.2;\n }\n }\n\n vec3 shaded = ambient + direct;\n if (emission > 0.5)\n shaded += albedo * vec3(0.18, 0.62, 0.12);\n if (bounces_enabled != 0) {\n float reflective = floor(mod(normal_light.w, 8192.0) / 4096.0);\n float refractive = floor(mod(normal_light.w, 16384.0) / 8192.0);\n if (reflective > 0.5) {\n vec3 reflected = secondary_radiance(point + normal * 0.12,\n reflect(direction, normal), hit);\n float fresnel = pow(1.0 - max(dot(-direction, normal), 0.0), 5.0);\n float reflection_mix = refractive > 0.5\n ? mix(0.34, 0.72, fresnel)\n : mix(0.42, 0.68, fresnel);\n shaded = mix(shaded, reflected, reflection_mix);\n } else {\n vec3 bounced = secondary_radiance(point + normal * 0.12,\n diffuse_bounce_direction(normal, float(hit)), hit);\n shaded += albedo * bounced * 0.18;\n }\n if (refractive > 0.5) {\n vec3 transmitted_direction = refract(direction, normal, 1.0 / 1.33);\n if (length(transmitted_direction) > 0.01) {\n vec3 transmitted = secondary_radiance(point - normal * 0.12,\n transmitted_direction, hit);\n shaded = mix(shaded, transmitted * vec3(0.72, 0.92, 0.74), 0.08);\n }\n }\n }\n if (fog_enabled != 0) {\n float fog = clamp(1.0 - exp(-nearest * \#{FOG_DENSITY}), 0.0, \#{FOG_MAX});\n shaded = mix(shaded, vec3(0.020, 0.025, 0.035), fog);\n }\n gl_FragColor = vec4(shaded, 1.0);\n}\n"- SPRITE_VERTEX_SHADER =
"#version 120\nvarying vec2 sprite_uv;\nvarying vec3 world_position;\nvoid main() {\n sprite_uv = gl_MultiTexCoord0.xy;\n world_position = gl_Vertex.xyz;\n gl_Position = ftransform();\n}\n"- SPRITE_FRAGMENT_SHADER =
"#version 120\nvarying vec2 sprite_uv;\nvarying vec3 world_position;\nuniform sampler2D sprite_texture;\nuniform float sector_light;\nuniform int light_count;\nuniform vec4 light_positions[\#{MAX_RAY_LIGHTS}];\nuniform vec4 light_colors[\#{MAX_RAY_LIGHTS}];\nuniform vec3 camera_position;\nuniform vec3 camera_forward;\nuniform int fog_enabled;\nuniform int flashlight_enabled;\n\nvoid main() {\n vec4 texel = texture2D(sprite_texture, sprite_uv);\n if (texel.a < 0.01) discard;\n vec3 shaded = texel.rgb * clamp(sector_light, 0.10, 1.0) * \#{AMBIENT_LEVEL};\n for (int light_index = 0; light_index < \#{MAX_RAY_LIGHTS}; ++light_index) {\n if (light_index >= light_count) break;\n float distance_to_light = length(light_positions[light_index].xyz - world_position);\n float attenuation = 1.0 / (1.0 + distance_to_light * 0.0015 +\n distance_to_light * distance_to_light * 0.000004);\n shaded += texel.rgb * light_colors[light_index].rgb * attenuation * 0.75;\n }\n vec3 camera_to_point = world_position - camera_position;\n float distance_to_camera = length(camera_to_point);\n if (flashlight_enabled != 0 && distance_to_camera > 0.001) {\n float cone = smoothstep(0.80, 0.96,\n dot(camera_to_point / distance_to_camera, camera_forward));\n float attenuation = 1.0 / (1.0 + distance_to_camera * 0.0015 +\n distance_to_camera * distance_to_camera * 0.000002);\n shaded += texel.rgb * vec3(1.0, 0.88, 0.68) * cone * attenuation * 1.5;\n }\n if (fog_enabled != 0) {\n float fog = clamp(1.0 - exp(-distance_to_camera * \#{FOG_DENSITY}), 0.0, \#{FOG_MAX});\n shaded = mix(shaded, vec3(0.020, 0.025, 0.035), fog);\n }\n gl_FragColor = vec4(shaded, texel.a);\n}\n"
Constants inherited from HardwareRenderer
HardwareRenderer::ANIMATED_DECORATIONS, HardwareRenderer::MAX_LIGHTS, HardwareRenderer::STATIC_LIGHTS, HardwareRenderer::VERTEX_STRIDE
Constants inherited from Renderer
Doom::Render::Renderer::SKY_TEXTUREMID, Doom::Render::Renderer::SKY_XSCALE, Doom::Render::Renderer::SKY_YSCALE
Instance Attribute Summary collapse
-
#bounces_enabled ⇒ Object
Returns the value of attribute bounces_enabled.
-
#flashlight_enabled ⇒ Object
Returns the value of attribute flashlight_enabled.
-
#fog_enabled ⇒ Object
Returns the value of attribute fog_enabled.
Attributes inherited from HardwareRenderer
Attributes inherited from Renderer
#animations, #colormap, #combat, #cos_angle, #flats, #framebuffer, #hidden_things, #leveltime, #map, #monster_ai, #palette, #player_angle, #player_x, #player_y, #player_z, #players, #sin_angle, #skip_background_fill, #sprites, #textures, #view_player, #wad
Instance Method Summary collapse
- #draw_hardware(viewport_width, viewport_height) ⇒ Object
-
#initialize ⇒ RayTracingRenderer
constructor
A new instance of RayTracingRenderer.
-
#light_color_at(x, y, z) ⇒ Object
Approximate RGB light reaching a point: sector ambient, the nearest point lights by distance falloff, and the flashlight (always on a held object right in front of the eye).
- #ray_tracing? ⇒ Boolean
- #render_frame ⇒ Object
Methods inherited from HardwareRenderer
Methods inherited from Renderer
#apply_view, #build_native_renderer, #check_plane, #draw_all_visplanes, #draw_floor_ceiling_background, #draw_sky_plane, #draw_span, #fill_uncovered_with_sector, #find_or_create_visplane, #native_wall_pass, #precompute_column_data, #render_visplane_spans, #set_player, #sprite_diagnostics
Constructor Details
#initialize ⇒ RayTracingRenderer
Returns a new instance of RayTracingRenderer.
517 518 519 520 521 522 523 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 517 def initialize(...) super @ray_materials = RayTracing::MaterialState.new(@flats, @animations) @fog_enabled = true @flashlight_enabled = true @bounces_enabled = true end |
Instance Attribute Details
#bounces_enabled ⇒ Object
Returns the value of attribute bounces_enabled.
515 516 517 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 515 def bounces_enabled @bounces_enabled end |
#flashlight_enabled ⇒ Object
Returns the value of attribute flashlight_enabled.
515 516 517 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 515 def flashlight_enabled @flashlight_enabled end |
#fog_enabled ⇒ Object
Returns the value of attribute fog_enabled.
515 516 517 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 515 def fog_enabled @fog_enabled end |
Instance Method Details
#draw_hardware(viewport_width, viewport_height) ⇒ Object
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 536 def draw_hardware(, ) Gosu.gl do load_opengl_library current_animation = @ray_materials.animation_signature if @ray_animation_signature != current_animation @ray_animation_signature = current_animation @ray_materials_dirty = true if @ray_data_texture end build_ray_scene if @ray_program.nil? || @ray_scene_dirty if @ray_materials_dirty upload_triangle_data @ray_materials_dirty = false end ensure_ray_target = [0, 0, 0, 0].pack('l4') glGetIntegerv(GL_VIEWPORT, ) , , physical_width, physical_height = .unpack('l4') glDisable(GL_DEPTH_TEST) glDisable(GL_CULL_FACE) glDisable(GL_LIGHTING) glClearColor(0.0, 0.0, 0.0, 1.0) glBindFramebuffer(GL_FRAMEBUFFER, @ray_framebuffer) glViewport(0, 0, RAY_WIDTH, RAY_HEIGHT) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) draw_ray_pass(RAY_WIDTH, RAY_HEIGHT) glBindFramebuffer(GL_FRAMEBUFFER, 0) glViewport(, , physical_width, physical_height) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) draw_ray_target setup_camera(, ) rebuild_gpu_batches if @gpu_batches.nil? || @gpu_batches_dirty glClear(GL_DEPTH_BUFFER_BIT) draw_occluder_depth_prepass(include_ceilings: true) draw_sprites capture_frame if ENV['DOOM_GL_CAPTURE'] && !@frame_captured # Gosu draws the weapon, HUD and pause menu immediately after this # block. Do not leak sprite-shader or modulation state into its 2D # pipeline, otherwise menu text inherits scene lighting. glUseProgram(0) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, 0) glColor4f(1.0, 1.0, 1.0, 1.0) glDisable(GL_LIGHTING) glDisable(GL_ALPHA_TEST) glDisable(GL_BLEND) glDisable(GL_TEXTURE_2D) glDisable(GL_DEPTH_TEST) end end |
#light_color_at(x, y, z) ⇒ Object
Approximate RGB light reaching a point: sector ambient, the nearest point lights by distance falloff, and the flashlight (always on a held object right in front of the eye). Used to tint the weapon so lamps and the beam colour the player's hand. Values may exceed 1.0; the caller clamps.
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 777 def light_color_at(x, y, z) sector = @map.sector_at(x, y) ambient = ((sector&.light_level || 128).to_f / 255.0) * AMBIENT_LEVEL color = [ambient, ambient, ambient] ray_lights.first(MAX_RAY_LIGHTS).each do |light| dx = light[:x] - x dy = light[:y] - y dz = light[:z] - z distance_sq = (dx * dx) + (dy * dy) + (dz * dz) attenuation = 1.0 / (1.0 + (Math.sqrt(distance_sq) * 0.0015) + (distance_sq * 0.000004)) light[:color].each_with_index { |channel, index| color[index] += channel * attenuation } end if @flashlight_enabled color[0] += 0.70 color[1] += 0.62 color[2] += 0.48 end color end |
#ray_tracing? ⇒ Boolean
511 512 513 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 511 def ray_tracing? true end |
#render_frame ⇒ Object
525 526 527 528 529 530 531 532 533 534 |
# File 'lib/doom/render/ray_tracing_renderer.rb', line 525 def render_frame signature = geometry_signature if signature != @geometry_signature @mesh = WorldMesh.new(@map, @textures) @geometry_signature = signature @ray_scene_dirty = true @gpu_batches_dirty = true end @framebuffer.fill(0) end |