Compare commits
7 Commits
7671e04adc
...
c39a871bf8
| Author | SHA1 | Date | |
|---|---|---|---|
| c39a871bf8 | |||
| 84b8e9c9a5 | |||
| ae6b1f6b0d | |||
| 9a9a88360a | |||
| c1b3e862d6 | |||
| c93ab42458 | |||
| b637590308 |
|
|
@ -2,7 +2,7 @@ use std::error::Error;
|
||||||
|
|
||||||
use glam::uvec2;
|
use glam::uvec2;
|
||||||
use raytracing3::present::{self, Presenter};
|
use raytracing3::present::{self, Presenter};
|
||||||
use raytracing3::scene::{load_envmap, Renderer, SceneParams};
|
use raytracing3::scene::{Renderer, SceneParams};
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{Event, WindowEvent},
|
event::{Event, WindowEvent},
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
|
|
@ -21,13 +21,12 @@ fn main() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (device, queue, surface) = pollster::block_on(init_gpu(window)).unwrap();
|
let (device, queue, surface) = pollster::block_on(init_gpu(window)).unwrap();
|
||||||
let envmap = load_envmap(&device, &queue);
|
|
||||||
queue.submit([]);
|
queue.submit([]);
|
||||||
|
|
||||||
let output_format = wgpu::TextureFormat::Bgra8UnormSrgb;
|
let output_format = wgpu::TextureFormat::Bgra8UnormSrgb;
|
||||||
let hdr_format = wgpu::TextureFormat::Rgba16Float;
|
let hdr_format = wgpu::TextureFormat::Rgba16Float;
|
||||||
let scene = SceneParams::new(N_SPHERES);
|
let scene = SceneParams::new(N_SPHERES);
|
||||||
let renderer = Renderer::new(&device, envmap);
|
let renderer = Renderer::new(&device);
|
||||||
let presenter = Presenter::new(&device, output_format);
|
let presenter = Presenter::new(&device, output_format);
|
||||||
|
|
||||||
let mut frame = 0;
|
let mut frame = 0;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use std::{fs, io};
|
||||||
use glam::{uvec2, UVec2};
|
use glam::{uvec2, UVec2};
|
||||||
use image::buffer::ConvertBuffer;
|
use image::buffer::ConvertBuffer;
|
||||||
use raytracing3::present::{self, Presenter};
|
use raytracing3::present::{self, Presenter};
|
||||||
use raytracing3::scene::{load_envmap, Renderer, SceneParams};
|
use raytracing3::scene::{Renderer, SceneParams};
|
||||||
|
|
||||||
const SIZE: UVec2 = uvec2(1920, 1080);
|
const SIZE: UVec2 = uvec2(1920, 1080);
|
||||||
const FRAME_RATE: u32 = 60;
|
const FRAME_RATE: u32 = 60;
|
||||||
|
|
@ -82,7 +82,6 @@ fn do_work(img_sender: async_channel::Sender<Frame>, start_frame: u32, stop_fram
|
||||||
let img_sender = Arc::new(img_sender);
|
let img_sender = Arc::new(img_sender);
|
||||||
|
|
||||||
let (device, queue) = pollster::block_on(init_gpu()).unwrap();
|
let (device, queue) = pollster::block_on(init_gpu()).unwrap();
|
||||||
let envmap = load_envmap(&device, &queue);
|
|
||||||
queue.submit([]);
|
queue.submit([]);
|
||||||
|
|
||||||
let texsize = wgpu::Extent3d {
|
let texsize = wgpu::Extent3d {
|
||||||
|
|
@ -95,7 +94,7 @@ fn do_work(img_sender: async_channel::Sender<Frame>, start_frame: u32, stop_fram
|
||||||
let hdr_format = wgpu::TextureFormat::Rgba16Float;
|
let hdr_format = wgpu::TextureFormat::Rgba16Float;
|
||||||
|
|
||||||
let scene = SceneParams::new(N_SPHERES);
|
let scene = SceneParams::new(N_SPHERES);
|
||||||
let renderer = Renderer::new(&device, envmap);
|
let renderer = Renderer::new(&device);
|
||||||
let presenter = Presenter::new(&device, output_format);
|
let presenter = Presenter::new(&device, output_format);
|
||||||
|
|
||||||
println!("Rendering...");
|
println!("Rendering...");
|
||||||
|
|
|
||||||
131
src/scene.rs
131
src/scene.rs
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::anim::{self, SphereParams};
|
use crate::anim::{self, SphereParams};
|
||||||
use crate::trace::{self, Tracer, TracerData, TracerEnv};
|
use crate::trace::{self, Tracer, TracerData};
|
||||||
|
use crate::Sphere;
|
||||||
use glam::{mat3, uvec2, vec3, UVec2, Vec3};
|
use glam::{mat3, uvec2, vec3, UVec2, Vec3};
|
||||||
use image::ImageReader;
|
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct CamLoc {
|
struct CamLoc {
|
||||||
eye: Vec3,
|
eye: Vec3,
|
||||||
forward: Vec3,
|
forward: Vec3,
|
||||||
|
|
@ -33,47 +34,39 @@ const CAMERA_LAG: f32 = 0.03;
|
||||||
|
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
tracer: Tracer,
|
tracer: Tracer,
|
||||||
env: TracerEnv,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
pub fn new(device: &wgpu::Device, env: wgpu::TextureView) -> Self {
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
let hdr_format = wgpu::TextureFormat::Rgba16Float;
|
let hdr_format = wgpu::TextureFormat::Rgba16Float;
|
||||||
let tracer = Tracer::new(&device, hdr_format);
|
let tracer = Tracer::new(&device, hdr_format);
|
||||||
let env = TracerEnv::new(&device, &tracer, &env);
|
Self { tracer }
|
||||||
Self { tracer, env }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SceneParams {
|
pub struct SceneParams {
|
||||||
pub spheres: Vec<SphereParams>,
|
spheres: Vec<Sphere>,
|
||||||
pub camera: SphereParams,
|
camera: CamLoc,
|
||||||
pub target: SphereParams,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SceneParams {
|
impl SceneParams {
|
||||||
pub fn new(n_spheres: u32) -> Self {
|
pub fn new(n_spheres: u32) -> Self {
|
||||||
let mut rng = rand_pcg::Pcg32::new(42, 0);
|
let mut rng = rand_pcg::Pcg32::new(42, 0);
|
||||||
let spheres: Vec<_> = {
|
const R: f32 = 100.;
|
||||||
let distr = anim::distr();
|
let sphere = Sphere {
|
||||||
(0..n_spheres).map(|_| distr(&mut rng)).collect()
|
center: vec3(0., 0., -R),
|
||||||
|
radius: R,
|
||||||
|
emit_color: vec3(0., 0., 0.),
|
||||||
|
reflect_color: Vec3::splat(0.3),
|
||||||
|
glossiness: 0.,
|
||||||
};
|
};
|
||||||
let camera = {
|
let spheres = vec![sphere];
|
||||||
let mut p = anim::distr()(&mut rng);
|
let camera = CamLoc {
|
||||||
p.amplitudes *= 2.0;
|
eye: vec3(-1., -1., 1.),
|
||||||
p.frequencies *= 0.1;
|
forward: vec3(1., 1., 0.).normalize(),
|
||||||
p
|
right: vec3(1., -1., 0.).normalize(),
|
||||||
};
|
};
|
||||||
let target = {
|
Self { spheres, camera }
|
||||||
let mut p = spheres[0];
|
|
||||||
p.phases -= 2. * PI * CAMERA_LAG * p.frequencies;
|
|
||||||
p
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
spheres,
|
|
||||||
camera,
|
|
||||||
target,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,18 +96,12 @@ impl Renderer {
|
||||||
time: f32,
|
time: f32,
|
||||||
seed: u32,
|
seed: u32,
|
||||||
) {
|
) {
|
||||||
let target = scene.target.to_sphere(time).center;
|
|
||||||
let eye = scene.camera.to_sphere(time).center;
|
|
||||||
let right = scene.camera.deriv(time);
|
|
||||||
let forward = target - eye;
|
|
||||||
let viewport = make_viewport(size.x, size.y);
|
let viewport = make_viewport(size.x, size.y);
|
||||||
let location = convert_location(CamLoc { eye, forward, right });
|
let location = convert_location(scene.camera);
|
||||||
let spheres: Vec<_> = scene.spheres.iter().map(|p| p.to_sphere(time)).collect();
|
let data = TracerData::new(&device, &self.tracer, &scene.spheres);
|
||||||
let data = TracerData::new(&device, &self.tracer, &spheres);
|
|
||||||
self.tracer.render(
|
self.tracer.render(
|
||||||
&mut render_pass.0,
|
&mut render_pass.0,
|
||||||
&data,
|
&data,
|
||||||
&self.env,
|
|
||||||
trace::Params {
|
trace::Params {
|
||||||
max_reflections: 3,
|
max_reflections: 3,
|
||||||
min_strength: 0.1,
|
min_strength: 0.1,
|
||||||
|
|
@ -125,82 +112,8 @@ impl Renderer {
|
||||||
trace::Aperture {
|
trace::Aperture {
|
||||||
radius: 0.0003,
|
radius: 0.0003,
|
||||||
focal_distance: std::f32::INFINITY,
|
focal_distance: std::f32::INFINITY,
|
||||||
glare_strength: 0.1,
|
|
||||||
glare_radius: 0.1,
|
|
||||||
},
|
},
|
||||||
location,
|
location,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_envmap(device: &wgpu::Device, queue: &wgpu::Queue) -> wgpu::TextureView {
|
|
||||||
let imgs = std::thread::scope(|s| {
|
|
||||||
[0, 1, 2, 3, 4, 5]
|
|
||||||
.map(|face| {
|
|
||||||
s.spawn(move || {
|
|
||||||
let img = ImageReader::open(format!("textures/env{face}.webp"))
|
|
||||||
.unwrap()
|
|
||||||
.with_guessed_format()
|
|
||||||
.unwrap()
|
|
||||||
.decode()
|
|
||||||
.unwrap();
|
|
||||||
img.to_rgba8()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map(|t| t.join().unwrap())
|
|
||||||
});
|
|
||||||
let size = imgs[0].width();
|
|
||||||
for img in &imgs {
|
|
||||||
assert!(img.width() == size);
|
|
||||||
assert!(img.height() == size);
|
|
||||||
}
|
|
||||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
depth_or_array_layers: 6,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
|
||||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
|
||||||
view_formats: &[],
|
|
||||||
});
|
|
||||||
for (face, img) in imgs.iter().enumerate() {
|
|
||||||
queue.write_texture(
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
texture: &texture,
|
|
||||||
mip_level: 0,
|
|
||||||
origin: wgpu::Origin3d {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: face as u32,
|
|
||||||
},
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
},
|
|
||||||
img.as_raw(),
|
|
||||||
wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(4 * size),
|
|
||||||
rows_per_image: Some(size),
|
|
||||||
},
|
|
||||||
wgpu::Extent3d {
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
texture.create_view(&wgpu::TextureViewDescriptor {
|
|
||||||
label: None,
|
|
||||||
format: None,
|
|
||||||
dimension: Some(wgpu::TextureViewDimension::Cube),
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
base_mip_level: 0,
|
|
||||||
mip_level_count: None,
|
|
||||||
base_array_layer: 0,
|
|
||||||
array_layer_count: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
||||||
56
src/trace.rs
56
src/trace.rs
|
|
@ -22,8 +22,6 @@ pub struct Viewport {
|
||||||
pub struct Aperture {
|
pub struct Aperture {
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
pub focal_distance: f32, // from 0 (exclusive) to +∞ (inclusive)
|
pub focal_distance: f32, // from 0 (exclusive) to +∞ (inclusive)
|
||||||
pub glare_strength: f32,
|
|
||||||
pub glare_radius: f32, // at distance 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -50,8 +48,6 @@ struct CameraData {
|
||||||
aperture: f32,
|
aperture: f32,
|
||||||
eye: Vec3,
|
eye: Vec3,
|
||||||
antifocal: f32,
|
antifocal: f32,
|
||||||
glare_strength: f32,
|
|
||||||
glare_radius: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(Viewport, CameraLocation, Aperture)> for CameraData {
|
impl From<(Viewport, CameraLocation, Aperture)> for CameraData {
|
||||||
|
|
@ -65,8 +61,6 @@ impl From<(Viewport, CameraLocation, Aperture)> for CameraData {
|
||||||
height: value.0.corner.y / value.0.corner.z,
|
height: value.0.corner.y / value.0.corner.z,
|
||||||
aperture: value.2.radius,
|
aperture: value.2.radius,
|
||||||
antifocal: 1. / value.2.focal_distance,
|
antifocal: 1. / value.2.focal_distance,
|
||||||
glare_strength: value.2.glare_strength,
|
|
||||||
glare_radius: value.2.glare_radius,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,10 +113,6 @@ pub struct TracerData {
|
||||||
bindings: wgpu::BindGroup,
|
bindings: wgpu::BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TracerEnv {
|
|
||||||
bindings: wgpu::BindGroup,
|
|
||||||
}
|
|
||||||
|
|
||||||
static SHADER: &str = include_str!("trace.wgsl");
|
static SHADER: &str = include_str!("trace.wgsl");
|
||||||
|
|
||||||
impl Tracer {
|
impl Tracer {
|
||||||
|
|
@ -150,30 +140,9 @@ impl Tracer {
|
||||||
count: None,
|
count: None,
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
let envmap_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::Cube,
|
|
||||||
multisampled: false,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
bind_group_layouts: &[&spheres_bgl, &envmap_bgl],
|
bind_group_layouts: &[&spheres_bgl],
|
||||||
push_constant_ranges: &[wgpu::PushConstantRange {
|
push_constant_ranges: &[wgpu::PushConstantRange {
|
||||||
stages: wgpu::ShaderStages::FRAGMENT,
|
stages: wgpu::ShaderStages::FRAGMENT,
|
||||||
range: 0..mem::size_of::<ParamsData>() as u32,
|
range: 0..mem::size_of::<ParamsData>() as u32,
|
||||||
|
|
@ -258,7 +227,6 @@ impl Tracer {
|
||||||
&self,
|
&self,
|
||||||
pass: &mut wgpu::RenderPass,
|
pass: &mut wgpu::RenderPass,
|
||||||
data: &TracerData,
|
data: &TracerData,
|
||||||
env: &TracerEnv,
|
|
||||||
params: Params,
|
params: Params,
|
||||||
viewport: Viewport,
|
viewport: Viewport,
|
||||||
aperture: Aperture,
|
aperture: Aperture,
|
||||||
|
|
@ -275,7 +243,6 @@ impl Tracer {
|
||||||
);
|
);
|
||||||
pass.set_vertex_buffer(0, self.view_buf.slice(..));
|
pass.set_vertex_buffer(0, self.view_buf.slice(..));
|
||||||
pass.set_bind_group(0, &data.bindings, &[]);
|
pass.set_bind_group(0, &data.bindings, &[]);
|
||||||
pass.set_bind_group(1, &env.bindings, &[]);
|
|
||||||
pass.draw(0..4, 0..1);
|
pass.draw(0..4, 0..1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -299,24 +266,3 @@ impl TracerData {
|
||||||
Self { bindings }
|
Self { bindings }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TracerEnv {
|
|
||||||
pub fn new(device: &wgpu::Device, tracer: &Tracer, view: &wgpu::TextureView) -> Self {
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
|
|
||||||
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: &tracer.pipeline.get_bind_group_layout(1),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::TextureView(view),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
Self { bindings }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@ struct Params {
|
||||||
aperture: f32,
|
aperture: f32,
|
||||||
eye: vec3f,
|
eye: vec3f,
|
||||||
antifocal: f32,
|
antifocal: f32,
|
||||||
|
|
||||||
glare_strength: f32,
|
|
||||||
glare_radius: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Sphere {
|
struct Sphere {
|
||||||
|
|
@ -36,8 +33,6 @@ struct Varying {
|
||||||
|
|
||||||
var<push_constant> params: Params;
|
var<push_constant> params: Params;
|
||||||
@group(0) @binding(1) var<storage, read> spheres: array<Sphere>;
|
@group(0) @binding(1) var<storage, read> spheres: array<Sphere>;
|
||||||
@group(1) @binding(0) var env_sampler: sampler;
|
|
||||||
@group(1) @binding(1) var env_texture: texture_cube<f32>;
|
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn on_vertex(in: Vertex) -> Varying {
|
fn on_vertex(in: Vertex) -> Varying {
|
||||||
|
|
@ -84,13 +79,7 @@ fn trace_fragment(in: Varying) -> vec3f {
|
||||||
pos = params.eye + aperture_offset_abs;
|
pos = params.eye + aperture_offset_abs;
|
||||||
let off_px = vec2(rand_float(), rand_float()) - .5;
|
let off_px = vec2(rand_float(), rand_float()) - .5;
|
||||||
let off_w = mat2x3(dpdx(in.dir), dpdy(in.dir));
|
let off_w = mat2x3(dpdx(in.dir), dpdy(in.dir));
|
||||||
var dir = in.dir + off_w * off_px;
|
let dir = in.dir + off_w * off_px;
|
||||||
if (rand_float() < params.glare_strength) {
|
|
||||||
let p = rand_float();
|
|
||||||
let d = params.glare_radius * pow(p, 2.);
|
|
||||||
let glare_off = d * rand_disc();
|
|
||||||
dir += view_mtx * vec3(glare_off, 0.);
|
|
||||||
}
|
|
||||||
ray = normalize(dir - params.antifocal * aperture_offset_abs);
|
ray = normalize(dir - params.antifocal * aperture_offset_abs);
|
||||||
|
|
||||||
for (var k = 0; k < params.max_reflections; k++) {
|
for (var k = 0; k < params.max_reflections; k++) {
|
||||||
|
|
@ -104,8 +93,28 @@ fn trace_fragment(in: Varying) -> vec3f {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sphere == -1) {
|
if (sphere == -1) {
|
||||||
let env = textureSampleLevel(env_texture, env_sampler, ray, 0.0);
|
let theta = dot(ray, normalize(vec3(1., 2., 1.)));
|
||||||
result += 3.0 * color * env.xyz;
|
var env: vec3f; // in kilonits
|
||||||
|
|
||||||
|
const ILLUMINANCE_LUX = 1e5;
|
||||||
|
const ANGULAR_DIAMETER_DEG = 20.0; // Sun: 0.5°
|
||||||
|
|
||||||
|
const PI = 3.141592653589793;
|
||||||
|
|
||||||
|
const ANGULAR_DIAMETER_RAD = PI / 180.0 * ANGULAR_DIAMETER_DEG;
|
||||||
|
const THETA = 0.5 * ANGULAR_DIAMETER_RAD;
|
||||||
|
const COS_THETA = 1.0 - 0.5 * THETA * THETA; // approximately
|
||||||
|
|
||||||
|
const SOLID_ANGLE_SR = PI * THETA * THETA; // approximately
|
||||||
|
const LUMINANCE_NIT = ILLUMINANCE_LUX / SOLID_ANGLE_SR;
|
||||||
|
const LUMINANCE_KNIT = 1e-3 * LUMINANCE_NIT;
|
||||||
|
|
||||||
|
if (theta > COS_THETA) {
|
||||||
|
env = vec3(1.0, 0.9, 0.6) * LUMINANCE_KNIT;
|
||||||
|
} else {
|
||||||
|
env = mix(vec3(0.5, 1.0, 2.0), vec3(2.0, 3.0, 4.0), 0.5 * theta + 0.5);
|
||||||
|
}
|
||||||
|
result += color * env.xyz;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let s = spheres[sphere];
|
let s = spheres[sphere];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user