engine/render/vulkan: analytic lighting and PBR basics
We implement a simple PBR shader that uses off-the-shelf maths from [Kar13]. I attempt to trace and cite as much as possible, but the amount of different approaches from both industry and academia makes it quite difficult to find exact papers to cite - especially as my mathematical skills are not high enough to derive, or check the derivation for any of these myself. [Kar13] Brian Karis. 2013. "Real Shading in Unreal Engine 4" URL: https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdfecs
parent
1a52782882
commit
29ff5f6eb4
|
@ -13,6 +13,7 @@ rust_binary(
|
||||||
"src/main.rs",
|
"src/main.rs",
|
||||||
"src/physics/mod.rs",
|
"src/physics/mod.rs",
|
||||||
"src/physics/color.rs",
|
"src/physics/color.rs",
|
||||||
|
"src/render/light.rs",
|
||||||
"src/render/material.rs",
|
"src/render/material.rs",
|
||||||
"src/render/mesh.rs",
|
"src/render/mesh.rs",
|
||||||
"src/render/mod.rs",
|
"src/render/mod.rs",
|
||||||
|
|
|
@ -2,11 +2,197 @@
|
||||||
#version 450
|
#version 450
|
||||||
#extension GL_ARB_separate_shader_objects : enable
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
struct OmniLight {
|
||||||
|
vec4 pos;
|
||||||
|
vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(push_constant) uniform PushConstantObject {
|
||||||
|
mat4 view;
|
||||||
|
vec4 cameraPos;
|
||||||
|
OmniLight omniLights[3];
|
||||||
|
} pco;
|
||||||
|
|
||||||
|
|
||||||
layout(binding = 0) uniform sampler2D texSamplerDiffuse;
|
layout(binding = 0) uniform sampler2D texSamplerDiffuse;
|
||||||
layout(binding = 1) uniform sampler2D texSamplerRoughness;
|
layout(binding = 1) uniform sampler2D texSamplerRoughness;
|
||||||
|
|
||||||
layout(location = 0) in vec2 fragTexCoord;
|
layout(location = 0) in vec2 fragTexCoord;
|
||||||
|
layout(location = 1) in vec3 fragWorldPos;
|
||||||
|
layout(location = 2) in vec3 fragNormal;
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
void main() {
|
const float PI = 3.14159;
|
||||||
outColor = texture(texSamplerDiffuse, fragTexCoord) * texture(texSamplerRoughness, fragTexCoord).x;
|
|
||||||
|
// We implement a Lambertiand & Cook-Torrance BRDF-based lighting system.
|
||||||
|
// All of this is based on a number of scientific papers, meta-studies and modern sources. We do
|
||||||
|
// our best to cite as much as possible for future reference.
|
||||||
|
// Most of the maths is used straight from [Kar13].
|
||||||
|
//
|
||||||
|
// A good summary of different research is available this blog post by Brian Karis, that attempts
|
||||||
|
// to catalogue all existing BRDF-related functions:
|
||||||
|
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
|
||||||
|
|
||||||
|
/// Bibliography:
|
||||||
|
//
|
||||||
|
// [Bec63]
|
||||||
|
// P. Beckmann & A. Spizzichino. 1963. "The Scattering of Electromagnetic Waves from Rough Surfaces"
|
||||||
|
// MacMillan, New York
|
||||||
|
//
|
||||||
|
// [Smi67]
|
||||||
|
// Bruce Smith. 1967. "Geometrical shadowing of a random rough surface."
|
||||||
|
// IEEE transactions on antennas and propagation 15.5 (1967): 668-671.
|
||||||
|
//
|
||||||
|
// [CT82]
|
||||||
|
// Robert L. Cook, Kenneth E. Torrance. 1982. "A Reflectance Model for Computer Graphics"
|
||||||
|
// ACM Transactions on Graphics, 1(1), 7–24.
|
||||||
|
// doi: 10.1145/357290.357293
|
||||||
|
//
|
||||||
|
// [Sch94]
|
||||||
|
// Christophe Schlick. 1994. "An Inexpensive BRDF Model for Physically-based Rendering"
|
||||||
|
// Computer Graphics Forum, 13(3), 233–246.
|
||||||
|
// doi: 10.1111/1467-8659.1330233
|
||||||
|
//
|
||||||
|
// [Wa07]
|
||||||
|
// Bruce Walter et al. 2007. "Microfacet Models for Refraction through Rough Surfaces."
|
||||||
|
// Proceedings of the Eurographics Symposium on Rendering.
|
||||||
|
//
|
||||||
|
// [Bur12]
|
||||||
|
// Brent Burley. 2012. "Physically-Based Shading at Disney"
|
||||||
|
// URL: https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf
|
||||||
|
//
|
||||||
|
// [Kar13]
|
||||||
|
// Brian Karis. 2013. "Real Shading in Unreal Engine 4"
|
||||||
|
// URL: https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||||||
|
//
|
||||||
|
// [Hei14]
|
||||||
|
// Eric Heitz. 2014. "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
|
||||||
|
// Journal of Computer Graphics Techniques, 3 (2).
|
||||||
|
//
|
||||||
|
// [GA19]
|
||||||
|
// Romain Guy, Mathias Agopian, "Physically Based Rendering in Filament"
|
||||||
|
// URL: https://google.github.io/filament/Filament.html
|
||||||
|
|
||||||
|
// [Sch94] Fresnel approximation, used for F in Cook-Torrance BRDF.
|
||||||
|
vec3 FresnelSchlick(float HdotV, vec3 F0) {
|
||||||
|
return F0 + (1.0 - F0) * pow(1.0 - HdotV, 5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Microfacet Normal Distribution Function, used for D in Cook-Torrance BRDF.
|
||||||
|
float DistributionGGX(float NdotH, float roughness) {
|
||||||
|
// 'Roughness remapping' as per [Bur12]
|
||||||
|
float a = roughness * roughness;
|
||||||
|
|
||||||
|
// NDF from [Kar13], that cites [Bur12], which in turn cites [Wa07].
|
||||||
|
// However, I could not find the same equation form in [Bur12] or deduce it myself from [Wa07],
|
||||||
|
// and ended up taking the direct, untraceable form from [Kar13], so take this with a grain of salt.
|
||||||
|
float a2 = a * a;
|
||||||
|
float NdotH2 = NdotH * NdotH;
|
||||||
|
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
||||||
|
return (a * a) / (PI * denom * denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float GeometrySchlickGGX(float NdotV, float roughness) {
|
||||||
|
// Remapping of K for analytical (non-IBL) lighting per [Kar13].
|
||||||
|
float r = (roughness + 1.0);
|
||||||
|
float k = (r * r) / 8.0;
|
||||||
|
|
||||||
|
// [Sch94] approximation of [Smi67] equation for [Bec63].
|
||||||
|
return (NdotV) / (NdotV * (1.0 - k) + k);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Geometric shadowing function, used for G in Cook-Torrance BRDF.
|
||||||
|
float GeometrySmith(float NdotV, float NdotL, float roughness) {
|
||||||
|
// Smith geometric shadowing function.
|
||||||
|
// [GA19] cites [Hei14] as demonstrating [Smi97] to be correct.
|
||||||
|
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||||
|
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||||
|
return ggx1 * ggx2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cook-Torrance [CT82] specular model.
|
||||||
|
vec3 SpecularCookTorrance(float NdotH, float NdotV, float NdotL, vec3 F, float roughness) {
|
||||||
|
float NDF = DistributionGGX(NdotH, roughness);
|
||||||
|
float G = GeometrySmith(NdotV, NdotL, roughness);
|
||||||
|
// F is taken in as a pre-computed argument for optimization purposes (it's reused for the
|
||||||
|
// lambertian component of the lighting model).
|
||||||
|
|
||||||
|
// Form from [Kar13], decuced from [CT82].
|
||||||
|
vec3 specular = (NDF * G * F) / max((4.0 * NdotV * NdotL), 0.0001);
|
||||||
|
return specular;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 cameraPos = pco.cameraPos.xyz / pco.cameraPos.w;
|
||||||
|
|
||||||
|
// Normal of this fragment.
|
||||||
|
vec3 N = normalize(fragNormal);
|
||||||
|
// Unit vector pointing at camera from this fragment.
|
||||||
|
vec3 V = normalize(cameraPos - fragWorldPos);
|
||||||
|
|
||||||
|
// Texture parameters for this fragment.
|
||||||
|
vec3 albedo = texture(texSamplerDiffuse, fragTexCoord).xyz;
|
||||||
|
float roughness = texture(texSamplerRoughness, fragTexCoord).x;
|
||||||
|
float metallic = 0.0;
|
||||||
|
float dielectric = 1.0 - metallic;
|
||||||
|
|
||||||
|
// Absolute Specular Reflectance at normal incidence. Ie., the base reflectivity of a
|
||||||
|
// material when looking straight at it.
|
||||||
|
// Trick from https://learnopengl.com/PBR/Theory : encode the reflectivity in the albedo for
|
||||||
|
// metallic materials (as they have no albedo). Otherwise, default to a typical reflectivity
|
||||||
|
// for non-metallic (dielectric) materials (0.04).
|
||||||
|
vec3 F0 = vec3(0.04);
|
||||||
|
F0 = mix(F0, albedo, metallic);
|
||||||
|
|
||||||
|
// Luminance of this fragment.
|
||||||
|
// Luminance is defined as the sum (integral) of all ilncoming illuminance over the half-sphere
|
||||||
|
// 'above' that point. As we currently only support analytic lighting (ie. omni lights), we
|
||||||
|
// integrate by iterating over all luminance sources, that currently are point lights.
|
||||||
|
vec3 Lo = vec3(0.0);
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
vec3 lightPos = pco.omniLights[i].pos.xyz;
|
||||||
|
vec3 lightColor = pco.omniLights[i].color.xyz;
|
||||||
|
|
||||||
|
// Unit vector pointing at light from fragment.
|
||||||
|
vec3 L = normalize(lightPos - fragWorldPos);
|
||||||
|
// Half-vector between to-light and to-camera unit vectors.
|
||||||
|
vec3 H = normalize(V + L);
|
||||||
|
|
||||||
|
// Dot products re-used across further computation for this (fragment, light) pair.
|
||||||
|
float HdotV = max(dot(H, V), 0.0);
|
||||||
|
float NdotH = max(dot(N, H), 0.0);
|
||||||
|
float NdotV = max(dot(N, V), 0.0);
|
||||||
|
float NdotL = max(dot(N, L), 0.0);
|
||||||
|
|
||||||
|
// Translate luminous flux (lumen) into luminous intensity at this solid angle (candela).
|
||||||
|
// This follows the derivation in [GA19] (58).
|
||||||
|
float distance = length(lightPos - fragWorldPos);
|
||||||
|
vec3 intensity = (lightColor / (4 * PI * (distance * distance)));
|
||||||
|
|
||||||
|
// The Fresnel component from the Cook-Torrance specular BRDF is also used to calculate the
|
||||||
|
// lambertian diffuse weight kD. We calculate it outside of the function.
|
||||||
|
vec3 F = FresnelSchlick(HdotV, F0);
|
||||||
|
// Cook-Torrance specular value.
|
||||||
|
vec3 specular = SpecularCookTorrance(NdotH, NdotV, NdotL, F, roughness);
|
||||||
|
|
||||||
|
// Lambertian diffuse component, influenced by fresnel and dielectric/metalness.
|
||||||
|
vec3 kD = (vec3(1.0) - F) * dielectric;
|
||||||
|
// Lambertian diffuse value.
|
||||||
|
vec3 diffuse = albedo / PI;
|
||||||
|
|
||||||
|
// Illuminance for this point from this light is a result of scaling the luminous
|
||||||
|
// intensity of this light by the BRDL and by (N o L). This follows the definitions
|
||||||
|
// of illuminance and luminous intensity.
|
||||||
|
vec3 Li = (kD * diffuse + specular) * intensity * NdotL;
|
||||||
|
|
||||||
|
// Integration of luminance from illuminance.
|
||||||
|
Lo += Li;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ambient = vec3(0.03) * albedo;
|
||||||
|
vec3 color = ambient + Lo;
|
||||||
|
outColor = vec4(color, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,38 @@
|
||||||
// vim: set ft=glsl:
|
// vim: set ft=glsl:
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
struct OmniLight {
|
||||||
|
vec3 pos;
|
||||||
|
vec3 color;
|
||||||
|
};
|
||||||
|
|
||||||
layout(push_constant) uniform UniformBufferObject {
|
layout(push_constant) uniform UniformBufferObject {
|
||||||
mat4 view;
|
mat4 view;
|
||||||
|
vec3 cameraPos;
|
||||||
|
OmniLight omniLights[2];
|
||||||
} ubo;
|
} ubo;
|
||||||
|
|
||||||
layout(location = 0) in vec3 pos;
|
layout(location = 0) in vec3 pos;
|
||||||
layout(location = 1) in mat4 model;
|
layout(location = 1) in vec3 normal;
|
||||||
layout(location = 5) in vec2 tex;
|
layout(location = 2) in mat4 model;
|
||||||
|
layout(location = 6) in vec2 tex;
|
||||||
|
|
||||||
layout(location = 0) out vec2 fragTexCoord;
|
layout(location = 0) out vec2 fragTexCoord;
|
||||||
|
layout(location = 1) out vec3 fragWorldPos;
|
||||||
|
layout(location = 2) out vec3 fragNormal;
|
||||||
|
|
||||||
out gl_PerVertex {
|
out gl_PerVertex {
|
||||||
vec4 gl_Position;
|
vec4 gl_Position;
|
||||||
};
|
};
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = ubo.view * model * vec4(pos, 1.0);
|
vec4 world = model * vec4(pos, 1.0);
|
||||||
|
|
||||||
fragTexCoord = tex;
|
fragTexCoord = tex;
|
||||||
|
fragNormal = normal;
|
||||||
|
fragWorldPos = world.xyz / world.w;
|
||||||
|
|
||||||
|
gl_Position = ubo.view * world;
|
||||||
// Vulkanize
|
// Vulkanize
|
||||||
gl_Position.y = -gl_Position.y;
|
gl_Position.y = -gl_Position.y;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,10 @@ mod util;
|
||||||
mod physics;
|
mod physics;
|
||||||
|
|
||||||
use render::vulkan::data;
|
use render::vulkan::data;
|
||||||
|
use render::light::Omni;
|
||||||
use render::material::{Texture, PBRMaterialBuilder};
|
use render::material::{Texture, PBRMaterialBuilder};
|
||||||
use render::mesh::Mesh;
|
use render::mesh::Mesh;
|
||||||
use render::renderable::{Object, Renderable, ResourceManager};
|
use render::renderable::{Light, Object, Renderable, ResourceManager};
|
||||||
use physics::color;
|
use physics::color;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -23,35 +24,35 @@ fn main() {
|
||||||
|
|
||||||
let mesh = {
|
let mesh = {
|
||||||
let vertices = Arc::new(vec![
|
let vertices = Arc::new(vec![
|
||||||
data::Vertex::new([-0.5, -0.5, 0.5], [1.0, 1.0, 1.0], [1.0, 0.0]),
|
data::Vertex::new([-0.5, -0.5, 0.5], [ 0.0, 0.0, 1.0], [1.0, 0.0]),
|
||||||
data::Vertex::new([0.5, -0.5, 0.5], [1.0, 1.0, 0.0], [0.0, 0.0]),
|
data::Vertex::new([ 0.5, -0.5, 0.5], [ 0.0, 0.0, 1.0], [0.0, 0.0]),
|
||||||
data::Vertex::new([0.5, 0.5, 0.5], [0.0, 1.0, 1.0], [0.0, 1.0]),
|
data::Vertex::new([ 0.5, 0.5, 0.5], [ 0.0, 0.0, 1.0], [0.0, 1.0]),
|
||||||
data::Vertex::new([-0.5, 0.5, 0.5], [1.0, 0.0, 1.0], [1.0, 1.0]),
|
data::Vertex::new([-0.5, 0.5, 0.5], [ 0.0, 0.0, 1.0], [1.0, 1.0]),
|
||||||
|
|
||||||
data::Vertex::new([0.5, -0.5, -0.5], [1.0, 1.0, 1.0], [0.0, 1.0]),
|
data::Vertex::new([ 0.5, -0.5, -0.5], [ 1.0, 0.0, 0.0], [0.0, 1.0]),
|
||||||
data::Vertex::new([0.5, 0.5, -0.5], [1.0, 1.0, 0.0], [1.0, 1.0]),
|
data::Vertex::new([ 0.5, 0.5, -0.5], [ 1.0, 0.0, 0.0], [1.0, 1.0]),
|
||||||
data::Vertex::new([0.5, 0.5, 0.5], [0.0, 1.0, 1.0], [1.0, 0.0]),
|
data::Vertex::new([ 0.5, 0.5, 0.5], [ 1.0, 0.0, 0.0], [1.0, 0.0]),
|
||||||
data::Vertex::new([0.5, -0.5, 0.5], [1.0, 0.0, 1.0], [0.0, 0.0]),
|
data::Vertex::new([ 0.5, -0.5, 0.5], [ 1.0, 0.0, 0.0], [0.0, 0.0]),
|
||||||
|
|
||||||
data::Vertex::new([-0.5, -0.5, -0.5], [1.0, 1.0, 1.0], [1.0, 1.0]),
|
data::Vertex::new([-0.5, -0.5, -0.5], [-1.0, 0.0, 0.0], [1.0, 1.0]),
|
||||||
data::Vertex::new([-0.5, 0.5, -0.5], [1.0, 1.0, 0.0], [0.0, 1.0]),
|
data::Vertex::new([-0.5, 0.5, -0.5], [-1.0, 0.0, 0.0], [0.0, 1.0]),
|
||||||
data::Vertex::new([-0.5, 0.5, 0.5], [0.0, 1.0, 1.0], [0.0, 0.0]),
|
data::Vertex::new([-0.5, 0.5, 0.5], [-1.0, 0.0, 0.0], [0.0, 0.0]),
|
||||||
data::Vertex::new([-0.5, -0.5, 0.5], [1.0, 0.0, 1.0], [1.0, 0.0]),
|
data::Vertex::new([-0.5, -0.5, 0.5], [-1.0, 0.0, 0.0], [1.0, 0.0]),
|
||||||
|
|
||||||
data::Vertex::new([-0.5, -0.5, -0.5], [1.0, 1.0, 1.0], [0.0, 1.0]),
|
data::Vertex::new([-0.5, -0.5, -0.5], [ 0.0, -1.0, 0.0], [0.0, 1.0]),
|
||||||
data::Vertex::new([0.5, -0.5, -0.5], [1.0, 1.0, 0.0], [1.0, 1.0]),
|
data::Vertex::new([ 0.5, -0.5, -0.5], [ 0.0, -1.0, 0.0], [1.0, 1.0]),
|
||||||
data::Vertex::new([0.5, -0.5, 0.5], [0.0, 1.0, 1.0], [1.0, 0.0]),
|
data::Vertex::new([ 0.5, -0.5, 0.5], [ 0.0, -1.0, 0.0], [1.0, 0.0]),
|
||||||
data::Vertex::new([-0.5, -0.5, 0.5], [1.0, 0.0, 1.0], [0.0, 0.0]),
|
data::Vertex::new([-0.5, -0.5, 0.5], [ 0.0, -1.0, 0.0], [0.0, 0.0]),
|
||||||
|
|
||||||
data::Vertex::new([-0.5, 0.5, -0.5], [1.0, 1.0, 1.0], [1.0, 1.0]),
|
data::Vertex::new([-0.5, 0.5, -0.5], [ 0.0, 1.0, 0.0], [1.0, 1.0]),
|
||||||
data::Vertex::new([0.5, 0.5, -0.5], [1.0, 1.0, 0.0], [0.0, 1.0]),
|
data::Vertex::new([ 0.5, 0.5, -0.5], [ 0.0, 1.0, 0.0], [0.0, 1.0]),
|
||||||
data::Vertex::new([0.5, 0.5, 0.5], [0.0, 1.0, 1.0], [0.0, 0.0]),
|
data::Vertex::new([ 0.5, 0.5, 0.5], [ 0.0, 1.0, 0.0], [0.0, 0.0]),
|
||||||
data::Vertex::new([-0.5, 0.5, 0.5], [1.0, 0.0, 1.0], [1.0, 0.0]),
|
data::Vertex::new([-0.5, 0.5, 0.5], [ 0.0, 1.0, 0.0], [1.0, 0.0]),
|
||||||
|
|
||||||
data::Vertex::new([-0.5, -0.5, -0.5], [1.0, 1.0, 1.0], [0.0, 0.0]),
|
data::Vertex::new([-0.5, -0.5, -0.5], [ 0.0, 0.0, -1.0], [0.0, 0.0]),
|
||||||
data::Vertex::new([0.5, -0.5, -0.5], [1.0, 1.0, 0.0], [1.0, 0.0]),
|
data::Vertex::new([ 0.5, -0.5, -0.5], [ 0.0, 0.0, -1.0], [1.0, 0.0]),
|
||||||
data::Vertex::new([0.5, 0.5, -0.5], [0.0, 1.0, 1.0], [1.0, 1.0]),
|
data::Vertex::new([ 0.5, 0.5, -0.5], [ 0.0, 0.0, -1.0], [1.0, 1.0]),
|
||||||
data::Vertex::new([-0.5, 0.5, -0.5], [1.0, 0.0, 1.0], [0.0, 1.0]),
|
data::Vertex::new([-0.5, 0.5, -0.5], [ 0.0, 0.0, -1.0], [0.0, 1.0]),
|
||||||
]);
|
]);
|
||||||
let indices = Arc::new(vec![
|
let indices = Arc::new(vec![
|
||||||
0, 1, 2, 2, 3, 0,
|
0, 1, 2, 2, 3, 0,
|
||||||
|
@ -87,7 +88,13 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let renderables: Vec<Box<dyn Renderable>> = cubes.into_iter().map(|e| e as Box<dyn Renderable>).collect();
|
let light1 = rm.add_light(Omni::test(cgm::Vector3::new(-10.0, -10.0, -5.0)));
|
||||||
|
let light2 = rm.add_light(Omni::test(cgm::Vector3::new(10.0, 10.0, -5.0)));
|
||||||
|
|
||||||
|
|
||||||
|
let mut renderables: Vec<Box<dyn Renderable>> = cubes.into_iter().map(|e| e as Box<dyn Renderable>).collect();
|
||||||
|
renderables.push(Box::new(Light{ light: light1 }));
|
||||||
|
renderables.push(Box::new(Light{ light: light2 }));
|
||||||
|
|
||||||
let start = time::Instant::now();
|
let start = time::Instant::now();
|
||||||
let mut renderer = render::Renderer::initialize();
|
let mut renderer = render::Renderer::initialize();
|
||||||
|
@ -97,17 +104,29 @@ fn main() {
|
||||||
|
|
||||||
let position = (instant / 10.0) * 3.14 * 2.0;
|
let position = (instant / 10.0) * 3.14 * 2.0;
|
||||||
|
|
||||||
let view = cgm::Matrix4::look_at(
|
let camera = cgm::Point3::new(
|
||||||
cgm::Point3::new(
|
//position.cos() * 10.0 * (((position*2.0).cos()/2.0)+1.0),
|
||||||
position.cos() * 10.0 * (((position*2.0).cos()/2.0)+1.0),
|
//position.sin() * 10.0 * (((position*2.0).cos()/2.0)+1.0),
|
||||||
position.sin() * 10.0 * (((position*2.0).cos()/2.0)+1.0),
|
7.0 + (position / 4.0).sin(),
|
||||||
|
12.0 + (position / 4.0).cos(),
|
||||||
3.0
|
3.0
|
||||||
),
|
);
|
||||||
|
|
||||||
|
if let Some(light) = rm.light_mut(&light1) {
|
||||||
|
light.position = cgm::Vector3::new(
|
||||||
|
-0.0 + (position*3.0).sin() * 4.0,
|
||||||
|
-0.0 + (position*4.0).cos() * 4.0,
|
||||||
|
-0.0 + (position*2.0).sin() * 1.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let view = cgm::Matrix4::look_at(
|
||||||
|
camera.clone(),
|
||||||
cgm::Point3::new(0.0, 0.0, 0.0),
|
cgm::Point3::new(0.0, 0.0, 0.0),
|
||||||
cgm::Vector3::new(0.0, 0.0, 1.0)
|
cgm::Vector3::new(0.0, 0.0, 1.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
renderer.draw_frame(&view, &rm, &renderables);
|
renderer.draw_frame(&camera, &view, &rm, &renderables);
|
||||||
if renderer.poll_close() {
|
if renderer.poll_close() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
use std::time;
|
||||||
|
|
||||||
|
use cgmath as cgm;
|
||||||
|
|
||||||
|
use crate::physics::color;
|
||||||
|
use crate::render::vulkan::data;
|
||||||
|
|
||||||
|
/// An Omni point light, with position in 3d space, and 'color' defined in lumens per CIE XYZ
|
||||||
|
/// color channel.
|
||||||
|
pub struct Omni {
|
||||||
|
pub position: cgm::Vector3<f32>,
|
||||||
|
/// Luminour power/flux defined as lumens per XYZ color channel.
|
||||||
|
pub color: color::XYZ,
|
||||||
|
|
||||||
|
pub id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Omni {
|
||||||
|
/// Make a test light. This has... a color. It's kinda yellow. And something close to 650
|
||||||
|
/// lumens of luminous power.
|
||||||
|
// TODO(q3k): implement [Kry85] (eq. 68) somewhere in //engine/src/physics for generation
|
||||||
|
// of nice lights colours from color temperature.
|
||||||
|
//
|
||||||
|
// [Kry85]
|
||||||
|
// M. Krystek. 1985. "An algorithm to calculate correlated color temperature"
|
||||||
|
// Color Research & Application, 10 (1), 38–40.
|
||||||
|
pub fn test(position: cgm::Vector3<f32>) -> Self {
|
||||||
|
Self {
|
||||||
|
position,
|
||||||
|
color: color::XYZ::new(234.7, 214.1, 207.9),
|
||||||
|
// TODO: use a better method
|
||||||
|
id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vulkan_uniform(&self) -> data::OmniLight {
|
||||||
|
// TODO: cache this?
|
||||||
|
data::OmniLight {
|
||||||
|
pos: [self.position.x, self.position.y, self.position.z, 1.0],
|
||||||
|
color: [self.color.x, self.color.y, self.color.z, 0.0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,10 +15,11 @@ use vulkano_win::VkSurfaceBuild;
|
||||||
use vulkano::instance as vi;
|
use vulkano::instance as vi;
|
||||||
use vulkano::swapchain as vs;
|
use vulkano::swapchain as vs;
|
||||||
|
|
||||||
pub mod vulkan;
|
pub mod light;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod renderable;
|
pub mod renderable;
|
||||||
|
pub mod vulkan;
|
||||||
|
|
||||||
const WIDTH: u32 = 800;
|
const WIDTH: u32 = 800;
|
||||||
const HEIGHT: u32 = 600;
|
const HEIGHT: u32 = 600;
|
||||||
|
@ -52,11 +53,12 @@ impl Renderer {
|
||||||
|
|
||||||
pub fn draw_frame(
|
pub fn draw_frame(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
camera: &cgm::Point3<f32>,
|
||||||
view: &cgm::Matrix4<f32>,
|
view: &cgm::Matrix4<f32>,
|
||||||
rm: &renderable::ResourceManager,
|
rm: &renderable::ResourceManager,
|
||||||
renderables: &Vec<Box<dyn renderable::Renderable>>
|
renderables: &Vec<Box<dyn renderable::Renderable>>
|
||||||
) {
|
) {
|
||||||
self.instance.flip(view, rm, renderables);
|
self.instance.flip(camera, view, rm, renderables);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_close(&mut self) -> bool {
|
pub fn poll_close(&mut self) -> bool {
|
||||||
|
|
|
@ -3,18 +3,31 @@ use std::hash;
|
||||||
|
|
||||||
use cgmath as cgm;
|
use cgmath as cgm;
|
||||||
|
|
||||||
|
use crate::render::light::Omni;
|
||||||
use crate::render::material::Material;
|
use crate::render::material::Material;
|
||||||
use crate::render::mesh::Mesh;
|
use crate::render::mesh::Mesh;
|
||||||
|
|
||||||
pub struct ResourceManager {
|
pub struct ResourceManager {
|
||||||
meshes: HashMap<u64, Mesh>,
|
meshes: HashMap<u64, Mesh>,
|
||||||
materials: HashMap<u64, Material>,
|
materials: HashMap<u64, Material>,
|
||||||
|
lights: HashMap<u64, Omni>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum ResourceID {
|
pub enum ResourceID {
|
||||||
Material(u64),
|
Material(u64),
|
||||||
Mesh(u64),
|
Mesh(u64),
|
||||||
|
Light(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceID {
|
||||||
|
pub fn id(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
ResourceID::Material(i) => *i,
|
||||||
|
ResourceID::Mesh(i) => *i,
|
||||||
|
ResourceID::Light(i) => *i,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl hash::Hash for ResourceID {
|
impl hash::Hash for ResourceID {
|
||||||
|
@ -22,21 +35,14 @@ impl hash::Hash for ResourceID {
|
||||||
match self {
|
match self {
|
||||||
ResourceID::Material(i) => i.hash(state),
|
ResourceID::Material(i) => i.hash(state),
|
||||||
ResourceID::Mesh(i) => i.hash(state),
|
ResourceID::Mesh(i) => i.hash(state),
|
||||||
|
ResourceID::Light(i) => i.hash(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for ResourceID {
|
impl PartialEq for ResourceID {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
let this = match self {
|
self.id() == other.id()
|
||||||
ResourceID::Material(i) => i,
|
|
||||||
ResourceID::Mesh(i) => i,
|
|
||||||
};
|
|
||||||
let that = match other {
|
|
||||||
ResourceID::Material(i) => i,
|
|
||||||
ResourceID::Mesh(i) => i,
|
|
||||||
};
|
|
||||||
this == that
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +53,7 @@ impl<'a> ResourceManager {
|
||||||
Self {
|
Self {
|
||||||
meshes: HashMap::new(),
|
meshes: HashMap::new(),
|
||||||
materials: HashMap::new(),
|
materials: HashMap::new(),
|
||||||
|
lights: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +69,12 @@ impl<'a> ResourceManager {
|
||||||
ResourceID::Mesh(id)
|
ResourceID::Mesh(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_light(&mut self, t: Omni) -> ResourceID {
|
||||||
|
let id = t.id;
|
||||||
|
self.lights.insert(id, t);
|
||||||
|
ResourceID::Light(id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn material(&'a self, id: &ResourceID) -> Option<&'a Material> {
|
pub fn material(&'a self, id: &ResourceID) -> Option<&'a Material> {
|
||||||
if let ResourceID::Material(i) = id {
|
if let ResourceID::Material(i) = id {
|
||||||
return Some(self.materials.get(&i).unwrap());
|
return Some(self.materials.get(&i).unwrap());
|
||||||
|
@ -75,12 +88,29 @@ impl<'a> ResourceManager {
|
||||||
}
|
}
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn light(&'a self, id: &ResourceID) -> Option<&'a Omni> {
|
||||||
|
if let ResourceID::Light(i) = id {
|
||||||
|
return Some(self.lights.get(&i).unwrap());
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn light_mut(&'a mut self, id: &ResourceID) -> Option<&'a mut Omni> {
|
||||||
|
if let ResourceID::Light(i) = id {
|
||||||
|
return Some(self.lights.get_mut(&i).unwrap());
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderable {
|
pub trait Renderable {
|
||||||
fn render_data(&self) -> Option<(ResourceID, ResourceID, &cgm::Matrix4<f32>)> {
|
fn render_data(&self) -> Option<(ResourceID, ResourceID, &cgm::Matrix4<f32>)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
fn light_data(&self) -> Option<ResourceID> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
|
@ -95,3 +125,12 @@ impl Renderable for Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Light {
|
||||||
|
pub light: ResourceID,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderable for Light {
|
||||||
|
fn light_data(&self) -> Option<ResourceID> {
|
||||||
|
Some(self.light)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,18 +9,18 @@ use cgmath as cgm;
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
pos: [f32; 3],
|
pos: [f32; 3],
|
||||||
color: [f32; 3],
|
normal: [f32; 3],
|
||||||
tex: [f32; 2],
|
tex: [f32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vertex {
|
impl Vertex {
|
||||||
pub fn new(pos: [f32; 3], color: [f32; 3], tex: [f32; 2]) -> Self {
|
pub fn new(pos: [f32; 3], normal: [f32; 3], tex: [f32; 2]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pos, color, tex,
|
pos, normal, tex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vulkano::impl_vertex!(Vertex, pos, color, tex);
|
vulkano::impl_vertex!(Vertex, pos, normal, tex);
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
|
@ -37,9 +37,17 @@ impl Instance {
|
||||||
}
|
}
|
||||||
vulkano::impl_vertex!(Instance, model);
|
vulkano::impl_vertex!(Instance, model);
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct OmniLight {
|
||||||
|
pub pos: [f32; 4],
|
||||||
|
pub color: [f32; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct UniformBufferObject {
|
pub struct UniformBufferObject {
|
||||||
pub view: cgm::Matrix4<f32>,
|
pub view: cgm::Matrix4<f32>,
|
||||||
|
pub camera_pos: cgm::Vector4<f32>,
|
||||||
|
pub omni_lights: [OmniLight; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -148,6 +148,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
||||||
fn make_graphics_commands(
|
fn make_graphics_commands(
|
||||||
&mut self,
|
&mut self,
|
||||||
profiler: &mut Profiler,
|
profiler: &mut Profiler,
|
||||||
|
camera: &cgm::Point3<f32>,
|
||||||
view: &cgm::Matrix4<f32>,
|
view: &cgm::Matrix4<f32>,
|
||||||
rm: &renderable::ResourceManager,
|
rm: &renderable::ResourceManager,
|
||||||
renderables: &Vec<Box<dyn renderable::Renderable>>,
|
renderables: &Vec<Box<dyn renderable::Renderable>>,
|
||||||
|
@ -160,31 +161,43 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
||||||
0.1,
|
0.1,
|
||||||
1000.0
|
1000.0
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut buffers: Vec<Box<vc::AutoCommandBuffer>> = vec![];
|
let mut buffers: Vec<Box<vc::AutoCommandBuffer>> = vec![];
|
||||||
|
|
||||||
|
|
||||||
// Sort renderables by mesh and materialid.
|
|
||||||
let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4<f32>> = ResourceMap::new();
|
|
||||||
|
|
||||||
let ubo = data::UniformBufferObject {
|
|
||||||
view: proj * view,
|
|
||||||
};
|
|
||||||
|
|
||||||
profiler.end("mgc.prep");
|
profiler.end("mgc.prep");
|
||||||
|
|
||||||
|
|
||||||
|
// Sort renderables by mesh and materialid, and find lights.
|
||||||
|
let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4<f32>> = ResourceMap::new();
|
||||||
|
let mut omni_lights = [data::OmniLight{ pos: [0.0, 0.0, 0.0, 0.0], color: [0.0, 0.0, 0.0, 0.0]}; 3];
|
||||||
|
let mut omni_light_count = 0;
|
||||||
|
|
||||||
for r in renderables {
|
for r in renderables {
|
||||||
if let Some((mesh_id, material_id, transform)) = r.render_data() {
|
if let Some((mesh_id, material_id, transform)) = r.render_data() {
|
||||||
meshes.add((mesh_id, material_id), transform);
|
meshes.add((mesh_id, material_id), transform);
|
||||||
}
|
}
|
||||||
|
if let Some(light_id) = r.light_data() {
|
||||||
|
if omni_light_count < 3 {
|
||||||
|
let light = rm.light(&light_id).unwrap();
|
||||||
|
omni_lights[omni_light_count] = light.vulkan_uniform();
|
||||||
|
omni_light_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
profiler.end("mgc.sort");
|
profiler.end("mgc.sort");
|
||||||
|
|
||||||
|
|
||||||
let device = self.surface_binding().device.clone();
|
let device = self.surface_binding().device.clone();
|
||||||
let queue = self.surface_binding().graphics_queue.clone();
|
let queue = self.surface_binding().graphics_queue.clone();
|
||||||
let rp = self.swapchain_binding().render_pass.clone();
|
let rp = self.swapchain_binding().render_pass.clone();
|
||||||
|
|
||||||
let pipeline = self.pipeline.as_ref().unwrap().get_pipeline().clone();
|
let pipeline = self.pipeline.as_ref().unwrap().get_pipeline().clone();
|
||||||
|
|
||||||
|
let camera_pos = camera.to_homogeneous();
|
||||||
|
let ubo = data::UniformBufferObject {
|
||||||
|
camera_pos: camera_pos,
|
||||||
|
view: proj * view,
|
||||||
|
omni_lights,
|
||||||
|
};
|
||||||
|
|
||||||
for ((mesh_id, material_id), transforms) in meshes.resources {
|
for ((mesh_id, material_id), transforms) in meshes.resources {
|
||||||
let mesh = rm.mesh(&mesh_id).unwrap();
|
let mesh = rm.mesh(&mesh_id).unwrap();
|
||||||
let material = rm.material(&material_id).unwrap();
|
let material = rm.material(&material_id).unwrap();
|
||||||
|
@ -240,6 +253,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
||||||
// (╯°□°)╯︵ ┻━┻
|
// (╯°□°)╯︵ ┻━┻
|
||||||
pub fn flip(
|
pub fn flip(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
camera: &cgm::Point3<f32>,
|
||||||
view: &cgm::Matrix4<f32>,
|
view: &cgm::Matrix4<f32>,
|
||||||
rm: &renderable::ResourceManager,
|
rm: &renderable::ResourceManager,
|
||||||
renderables: &Vec<Box<dyn renderable::Renderable>>,
|
renderables: &Vec<Box<dyn renderable::Renderable>>,
|
||||||
|
@ -247,7 +261,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
||||||
let mut profiler = Profiler::new();
|
let mut profiler = Profiler::new();
|
||||||
|
|
||||||
// Build batch command buffer as early as possible.
|
// Build batch command buffer as early as possible.
|
||||||
let mut batches = self.make_graphics_commands(&mut profiler, view, rm, renderables);
|
let mut batches = self.make_graphics_commands(&mut profiler, camera, view, rm, renderables);
|
||||||
profiler.end("mgc");
|
profiler.end("mgc");
|
||||||
|
|
||||||
match &self.previous_frame_end {
|
match &self.previous_frame_end {
|
||||||
|
@ -258,7 +272,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
||||||
if !self.armed {
|
if !self.armed {
|
||||||
self.arm();
|
self.arm();
|
||||||
// Rearming means the batch is invalid - rebuild it.
|
// Rearming means the batch is invalid - rebuild it.
|
||||||
batches = self.make_graphics_commands(&mut profiler, view, rm, renderables);
|
batches = self.make_graphics_commands(&mut profiler, camera, view, rm, renderables);
|
||||||
}
|
}
|
||||||
profiler.end("arm");
|
profiler.end("arm");
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,15 @@ impl Forward {
|
||||||
name: Some(Cow::Borrowed("pos")),
|
name: Some(Cow::Borrowed("pos")),
|
||||||
},
|
},
|
||||||
vps::ShaderInterfaceDefEntry {
|
vps::ShaderInterfaceDefEntry {
|
||||||
location: 1..5, format: Format::R32G32B32A32Sfloat,
|
location: 1..2, format: Format::R32G32B32Sfloat,
|
||||||
|
name: Some(Cow::Borrowed("normal")),
|
||||||
|
},
|
||||||
|
vps::ShaderInterfaceDefEntry {
|
||||||
|
location: 2..6, format: Format::R32G32B32A32Sfloat,
|
||||||
name: Some(Cow::Borrowed("model")),
|
name: Some(Cow::Borrowed("model")),
|
||||||
},
|
},
|
||||||
vps::ShaderInterfaceDefEntry {
|
vps::ShaderInterfaceDefEntry {
|
||||||
location: 5..6, format: Format::R32G32Sfloat,
|
location: 6..7, format: Format::R32G32Sfloat,
|
||||||
name: Some(Cow::Borrowed("tex")),
|
name: Some(Cow::Borrowed("tex")),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -50,14 +54,23 @@ impl Forward {
|
||||||
location: 0..1, format: Format::R32G32Sfloat,
|
location: 0..1, format: Format::R32G32Sfloat,
|
||||||
name: Some(Cow::Borrowed("fragTexCoord")),
|
name: Some(Cow::Borrowed("fragTexCoord")),
|
||||||
},
|
},
|
||||||
|
vps::ShaderInterfaceDefEntry {
|
||||||
|
location: 1..2, format: Format::R32G32B32Sfloat,
|
||||||
|
name: Some(Cow::Borrowed("fragWorldPos")),
|
||||||
|
},
|
||||||
|
vps::ShaderInterfaceDefEntry {
|
||||||
|
location: 2..3, format: Format::R32G32B32Sfloat,
|
||||||
|
name: Some(Cow::Borrowed("fragNormal")),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
uniforms: vec![],
|
uniforms: vec![],
|
||||||
push_constants: vec![
|
push_constants: vec![
|
||||||
vdp::PipelineLayoutDescPcRange {
|
vdp::PipelineLayoutDescPcRange {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: 64usize,
|
size: 128usize,
|
||||||
stages: vdD::ShaderStages {
|
stages: vdD::ShaderStages {
|
||||||
vertex: true,
|
vertex: true,
|
||||||
|
fragment: true,
|
||||||
..vdD::ShaderStages::none()
|
..vdD::ShaderStages::none()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -72,6 +85,14 @@ impl Forward {
|
||||||
location: 0..1, format: Format::R32G32Sfloat,
|
location: 0..1, format: Format::R32G32Sfloat,
|
||||||
name: Some(Cow::Borrowed("fragTexCoord")),
|
name: Some(Cow::Borrowed("fragTexCoord")),
|
||||||
},
|
},
|
||||||
|
vps::ShaderInterfaceDefEntry {
|
||||||
|
location: 1..2, format: Format::R32G32B32Sfloat,
|
||||||
|
name: Some(Cow::Borrowed("fragWorldPos")),
|
||||||
|
},
|
||||||
|
vps::ShaderInterfaceDefEntry {
|
||||||
|
location: 2..3, format: Format::R32G32B32Sfloat,
|
||||||
|
name: Some(Cow::Borrowed("fragNormal")),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
outputs: vec![
|
outputs: vec![
|
||||||
vps::ShaderInterfaceDefEntry {
|
vps::ShaderInterfaceDefEntry {
|
||||||
|
@ -111,7 +132,17 @@ impl Forward {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
push_constants: vec![],
|
push_constants: vec![
|
||||||
|
vdp::PipelineLayoutDescPcRange {
|
||||||
|
offset: 0,
|
||||||
|
size: 128usize,
|
||||||
|
stages: vdD::ShaderStages {
|
||||||
|
vertex: true,
|
||||||
|
fragment: true,
|
||||||
|
..vdD::ShaderStages::none()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
}.load_into(device.clone()).expect("could not load fragment shader");
|
}.load_into(device.clone()).expect("could not load fragment shader");
|
||||||
|
|
||||||
let dimensions = [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32];
|
let dimensions = [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32];
|
||||||
|
|
Loading…
Reference in New Issue