Start support for multiple point light casters
ci/woodpecker/push/build Pipeline was successful
Details
ci/woodpecker/push/build Pipeline was successful
Details
This commit is contained in:
parent
d26e1ccfb4
commit
6510d5a7b9
|
@ -1,17 +1,193 @@
|
||||||
pub mod point;
|
pub mod point;
|
||||||
|
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
|
||||||
|
|
||||||
|
use edict::query::EpochOf;
|
||||||
pub use point::*;
|
pub use point::*;
|
||||||
|
use tracing::debug;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use crate::math::Transform;
|
use std::mem;
|
||||||
|
|
||||||
#[repr(C)]
|
use crate::{math::Transform, ecs::components::TransformComponent};
|
||||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct LightUniform {
|
pub struct LightBuffer {
|
||||||
pub position: glam::Vec3,
|
pub bind_group: wgpu::BindGroup,
|
||||||
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here
|
/// The buffer on the gpu storing the lights
|
||||||
pub(crate) _padding: u32,
|
buffer: wgpu::Buffer,
|
||||||
pub color: glam::Vec3,
|
device_limits: wgpu::Limits,
|
||||||
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here
|
/// The max amount of light casters that could fit in this buffer.
|
||||||
pub(crate) _padding2: u32,
|
pub max_count: usize,
|
||||||
|
/// The current amount of light casters in this buffer.
|
||||||
|
pub current_count: usize,
|
||||||
|
/// The buffer index for a specific entity/caster.
|
||||||
|
used_indexes: HashMap<edict::EntityId, usize>,
|
||||||
|
/// Indexes that were being used but are no longer needed.
|
||||||
|
dead_indexes: VecDeque<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightBuffer {
|
||||||
|
pub fn new<U>(device: &wgpu::Device, layout: &wgpu::BindGroupLayout, max_count: usize) -> Self
|
||||||
|
where
|
||||||
|
U: Default + bytemuck::Pod + bytemuck::Zeroable
|
||||||
|
{
|
||||||
|
let device_limits = device.limits();
|
||||||
|
|
||||||
|
let buffer = device.create_buffer(
|
||||||
|
&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Light buffer"),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
size: (mem::size_of::<U>() * max_count) as u64,//render_limits.max_uniform_buffer_binding_size as u64,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
/* label: Some("Light Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&[U::default()]),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, */
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/* let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("light_bind_group"),
|
||||||
|
}); */
|
||||||
|
|
||||||
|
let stride = device_limits.min_uniform_buffer_offset_alignment as usize + mem::size_of::<U>();
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::Buffer(
|
||||||
|
wgpu::BufferBinding {
|
||||||
|
buffer: &buffer,
|
||||||
|
offset: 0,
|
||||||
|
size: Some(NonZeroU64::new(stride as u64).unwrap())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("light_bind_group"),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
bind_group,
|
||||||
|
device_limits,
|
||||||
|
max_count,
|
||||||
|
current_count: 0,
|
||||||
|
used_indexes: HashMap::new(),
|
||||||
|
dead_indexes: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_light(&self, entity: edict::EntityId) -> bool {
|
||||||
|
self.used_indexes.contains_key(&entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update an existing light in the light buffer.
|
||||||
|
pub fn update_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) {
|
||||||
|
let buffer_idx = *self.used_indexes.get(&entity)
|
||||||
|
.expect("Entity for Light is not in buffer!");
|
||||||
|
|
||||||
|
let offset = buffer_idx as u64 * self.device_limits.min_uniform_buffer_offset_alignment as u64;
|
||||||
|
queue.write_buffer(&self.buffer, offset, bytemuck::bytes_of(light));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new light to the light buffer.
|
||||||
|
pub fn add_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) {
|
||||||
|
let buffer_idx = match self.dead_indexes.pop_front() {
|
||||||
|
Some(i) => i,
|
||||||
|
None => {
|
||||||
|
let i = self.current_count;
|
||||||
|
self.current_count += 1;
|
||||||
|
i
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
self.used_indexes.insert(entity, buffer_idx);
|
||||||
|
self.update_light(queue, entity, light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update, or add a new caster, to the light buffer.
|
||||||
|
pub fn update_or_add(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) {
|
||||||
|
if self.used_indexes.contains_key(&entity) {
|
||||||
|
self.update_light(queue, entity, light);
|
||||||
|
} else {
|
||||||
|
self.add_light(queue, entity, light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) {
|
||||||
|
todo!() // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prune_dead_lights(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) {
|
||||||
|
todo!() // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LightUniformBuffers {
|
||||||
|
pub bindgroup_layout: wgpu::BindGroupLayout,
|
||||||
|
pub point_lights: LightBuffer,
|
||||||
|
//spotlights: LightBuffer,
|
||||||
|
//directional_light
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightUniformBuffers {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let bindgroup_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("light_bind_group_layout"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let point_lights = LightBuffer::new::<PointLightUniform>(device, &bindgroup_layout, 10);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
bindgroup_layout,
|
||||||
|
point_lights,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_lights(&mut self, queue: &wgpu::Queue, world: &edict::World) {
|
||||||
|
let now_epoch = world.epoch();
|
||||||
|
for (entity, point_light, transform, light_epoch, transform_epoch)
|
||||||
|
in world.query::<(edict::Entities, &PointLight, &TransformComponent, EpochOf<PointLight>, EpochOf<TransformComponent>)>().iter() {
|
||||||
|
|
||||||
|
/* if !self.point_lights.has_light(entity) || light_epoch == now_epoch || transform_epoch == now_epoch {
|
||||||
|
debug!("Updated light after update!");
|
||||||
|
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
|
||||||
|
self.point_lights.update_or_add(queue, entity, &uniform);
|
||||||
|
} */
|
||||||
|
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
|
||||||
|
self.point_lights.update_or_add(queue, entity, &uniform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binds the light buffer to the render pass.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// * `render_pass` - The render pass to bind the buffers to.
|
||||||
|
// * `point_bind_index` - The bind group index that the point light buffers will be bound to.
|
||||||
|
/* pub fn bind_lights<'a, 'b: 'a>(&'a mut self, render_pass: &'b mut wgpu::RenderPass<'b>, point_bind_index: u32) {
|
||||||
|
render_pass.set_bind_group(point_bind_index, &self.point_lights.bind_group, &[]);
|
||||||
|
//render_pass.set_bind_group(3, &self.point_light_bind_group, &[]);
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::render::light::PointLightUniform;
|
||||||
|
|
||||||
use super::camera::{RenderCamera, CameraUniform};
|
use super::camera::{RenderCamera, CameraUniform};
|
||||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||||
use super::light::{LightUniform, PointLight};
|
use super::light::{PointLight, LightUniformBuffers};
|
||||||
use super::texture::RenderTexture;
|
use super::texture::RenderTexture;
|
||||||
use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers};
|
use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers};
|
||||||
use super::vertex::Vertex;
|
use super::vertex::Vertex;
|
||||||
|
@ -91,9 +91,7 @@ pub struct BasicRenderer {
|
||||||
default_texture_bind_group: BindGroup,
|
default_texture_bind_group: BindGroup,
|
||||||
depth_buffer_texture: RenderTexture,
|
depth_buffer_texture: RenderTexture,
|
||||||
|
|
||||||
point_light_buffer: wgpu::Buffer,
|
light_buffers: LightUniformBuffers,
|
||||||
point_light_bind_group_layout: BindGroupLayout,
|
|
||||||
point_light_bind_group: BindGroup,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicRenderer {
|
impl BasicRenderer {
|
||||||
|
@ -243,41 +241,6 @@ impl BasicRenderer {
|
||||||
next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 },
|
next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
let point_light_buffer = device.create_buffer_init(
|
|
||||||
&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some("Point Light Buffer"),
|
|
||||||
contents: bytemuck::cast_slice(&[PointLightUniform::default()]),
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let point_light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: None,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
label: Some("point_light_bind_group_layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let point_light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout: &point_light_bind_group_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: point_light_buffer.as_entire_binding(),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
label: Some("point_light_bind_group"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let camera_buffer = device.create_buffer_init(
|
let camera_buffer = device.create_buffer_init(
|
||||||
&wgpu::util::BufferInitDescriptor {
|
&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Camera Buffer"),
|
label: Some("Camera Buffer"),
|
||||||
|
@ -335,6 +298,8 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let light_uniform_buffers = LightUniformBuffers::new(&device);
|
||||||
|
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
window,
|
window,
|
||||||
surface,
|
surface,
|
||||||
|
@ -366,9 +331,7 @@ impl BasicRenderer {
|
||||||
depth_buffer_texture: depth_texture,
|
depth_buffer_texture: depth_texture,
|
||||||
entity_last_transforms: HashMap::new(),
|
entity_last_transforms: HashMap::new(),
|
||||||
|
|
||||||
point_light_buffer,
|
light_buffers: light_uniform_buffers,
|
||||||
point_light_bind_group_layout,
|
|
||||||
point_light_bind_group,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the default pipelines
|
// create the default pipelines
|
||||||
|
@ -376,7 +339,7 @@ impl BasicRenderer {
|
||||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
||||||
vec![super::vertex::Vertex::desc(),],
|
vec![super::vertex::Vertex::desc(),],
|
||||||
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout,
|
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout,
|
||||||
&s.point_light_bind_group_layout])));
|
&s.light_buffers.bindgroup_layout])));
|
||||||
s.render_pipelines = pipelines;
|
s.render_pipelines = pipelines;
|
||||||
|
|
||||||
s
|
s
|
||||||
|
@ -647,10 +610,7 @@ impl Renderer for BasicRenderer {
|
||||||
warn!("Missing camera!");
|
warn!("Missing camera!");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (point_light, transform) in main_world.query::<(&PointLight, &TransformComponent)>().iter() {
|
self.light_buffers.update_lights(&self.queue, &main_world);
|
||||||
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
|
|
||||||
self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[uniform]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||||
|
@ -711,7 +671,10 @@ impl Renderer for BasicRenderer {
|
||||||
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
|
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
|
||||||
|
|
||||||
// bind light
|
// bind light
|
||||||
render_pass.set_bind_group(3, &self.point_light_bind_group, &[]);
|
//render_pass.set_bind_group(3, &self.point_light_bind_group, &[]);
|
||||||
|
render_pass.set_bind_group(3, &self.light_buffers.point_lights.bind_group, &[]);
|
||||||
|
////self.light_buffers.bind_lights(&mut render_pass, 3);
|
||||||
|
|
||||||
|
|
||||||
// if this mesh uses indices, use them to draw the mesh
|
// if this mesh uses indices, use them to draw the mesh
|
||||||
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// Vertex shader
|
// Vertex shader
|
||||||
|
|
||||||
|
const max_light_count: i32 = 10;
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
@location(1) tex_coords: vec2<f32>,
|
@location(1) tex_coords: vec2<f32>,
|
||||||
|
@ -31,10 +33,10 @@ struct PointLight {
|
||||||
var<uniform> u_model_transform: mat4x4<f32>;
|
var<uniform> u_model_transform: mat4x4<f32>;
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var<uniform> camera: CameraUniform;
|
var<uniform> u_camera: CameraUniform;
|
||||||
|
|
||||||
@group(3) @binding(0)
|
@group(3) @binding(0)
|
||||||
var<uniform> point_light: PointLight;
|
var<uniform> u_point_lights: array<PointLight, max_light_count>;
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(
|
fn vs_main(
|
||||||
|
@ -43,7 +45,7 @@ fn vs_main(
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
|
|
||||||
out.tex_coords = model.tex_coords;
|
out.tex_coords = model.tex_coords;
|
||||||
out.clip_position = camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
|
out.clip_position = u_camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
|
||||||
|
|
||||||
out.world_normal = (u_model_transform * vec4<f32>(model.normal, 0.0)).xyz;
|
out.world_normal = (u_model_transform * vec4<f32>(model.normal, 0.0)).xyz;
|
||||||
|
|
||||||
|
@ -61,38 +63,45 @@ var t_diffuse: texture_2d<f32>;
|
||||||
@group(0)@binding(1)
|
@group(0)@binding(1)
|
||||||
var s_diffuse: sampler;
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
//@group(3) @binding(0)
|
|
||||||
//var<uniform> point_light: PointLight;
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||||
|
|
||||||
|
var light_res = vec3<f32>(0.0);
|
||||||
|
for (var i = 0; i < 1; i++) {
|
||||||
|
light_res += blinn_phong_point_light(in.world_position, in.world_normal, u_point_lights[i]);
|
||||||
|
}
|
||||||
|
let light_object_res = light_res * object_color.xyz;
|
||||||
|
|
||||||
|
return vec4<f32>(light_object_res, object_color.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_light: PointLight) -> vec3<f32> {
|
||||||
let light_color = point_light.color.xyz;
|
let light_color = point_light.color.xyz;
|
||||||
let light_pos = point_light.position.xyz;
|
let light_pos = point_light.position.xyz;
|
||||||
let camera_view_pos = camera.view_pos.xyz;
|
let camera_view_pos = u_camera.view_pos.xyz;
|
||||||
|
|
||||||
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
|
||||||
|
|
||||||
// We don't need (or want) much ambient light, so 0.1 is fine
|
// We don't need (or want) much ambient light, so 0.1 is fine
|
||||||
let ambient_strength = 0.1;
|
let ambient_strength = 0.1;
|
||||||
var ambient_color = light_color * ambient_strength;
|
var ambient_color = light_color * ambient_strength;
|
||||||
|
|
||||||
//// diffuse ////
|
//// diffuse ////
|
||||||
let light_dir = normalize(light_pos - in.world_position);
|
let light_dir = normalize(light_pos - world_pos);
|
||||||
|
|
||||||
let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0);
|
let diffuse_strength = max(dot(world_norm, light_dir), 0.0);
|
||||||
var diffuse_color = light_color * diffuse_strength;
|
var diffuse_color = light_color * diffuse_strength;
|
||||||
//// end of diffuse ////
|
//// end of diffuse ////
|
||||||
|
|
||||||
//// specular ////
|
//// specular ////
|
||||||
let view_dir = normalize(camera_view_pos - in.world_position);
|
let view_dir = normalize(camera_view_pos - world_pos);
|
||||||
let half_dir = normalize(view_dir + light_dir);
|
let half_dir = normalize(view_dir + light_dir);
|
||||||
|
|
||||||
let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0);
|
let specular_strength = pow(max(dot(world_norm, half_dir), 0.0), 32.0);
|
||||||
var specular_color = specular_strength * light_color;
|
var specular_color = specular_strength * light_color;
|
||||||
//// end of specular ////
|
//// end of specular ////
|
||||||
|
|
||||||
//// point light attenuation ////
|
//// point light attenuation ////
|
||||||
let distance = length(light_pos - in.world_position);
|
let distance = length(light_pos - world_pos);
|
||||||
let attenuation = 1.0 / (point_light.constant + point_light.linear * distance +
|
let attenuation = 1.0 / (point_light.constant + point_light.linear * distance +
|
||||||
point_light.quadratic * (distance * distance));
|
point_light.quadratic * (distance * distance));
|
||||||
|
|
||||||
|
@ -101,7 +110,5 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
specular_color *= attenuation * point_light.intensity;
|
specular_color *= attenuation * point_light.intensity;
|
||||||
//// end of point light attenuation ////
|
//// end of point light attenuation ////
|
||||||
|
|
||||||
let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
|
return (ambient_color + diffuse_color + specular_color);
|
||||||
|
}
|
||||||
return vec4<f32>(result, object_color.a);
|
|
||||||
}
|
|
Loading…
Reference in New Issue