Migrate GLSL to Slang, text shader
This commit is contained in:
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
|
"shader-slang.slang-language-extension",
|
||||||
"vadimcn.vscode-lldb",
|
"vadimcn.vscode-lldb",
|
||||||
"ziglang.vscode-zig",
|
"ziglang.vscode-zig",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
#version 460
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform sampler _EquirectangularSampler;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 1) uniform texture2D _EquirectangularTexture;
|
|
||||||
layout(set = 0, binding = 2, rgba16f) uniform writeonly restrict imageCube _CubemapImage;
|
|
||||||
|
|
||||||
const float INV_PI = 0.31830987;
|
|
||||||
const float HALF_INV_PI = 0.15915494;
|
|
||||||
|
|
||||||
const mat3x3 MATRIX_2D_TO_CUBE[6] = mat3x3[](
|
|
||||||
// Positive X
|
|
||||||
mat3x3(
|
|
||||||
0, 0, -1,
|
|
||||||
0, -1, 0,
|
|
||||||
1, 0, 0
|
|
||||||
),
|
|
||||||
// Negative X
|
|
||||||
mat3x3(
|
|
||||||
0, 0, 1,
|
|
||||||
0, -1, 0,
|
|
||||||
-1, 0, 0
|
|
||||||
),
|
|
||||||
// Positive Y
|
|
||||||
mat3x3(
|
|
||||||
1, 0, 0,
|
|
||||||
0, 0, 1,
|
|
||||||
0, 1, 0
|
|
||||||
),
|
|
||||||
// Negative Y
|
|
||||||
mat3x3(
|
|
||||||
1, 0, 0,
|
|
||||||
0, 0, -1,
|
|
||||||
0, -1, 0
|
|
||||||
),
|
|
||||||
// Positive Z
|
|
||||||
mat3x3(
|
|
||||||
1, 0, 0,
|
|
||||||
0, -1, 0,
|
|
||||||
0, 0, 1
|
|
||||||
),
|
|
||||||
// Negative Z
|
|
||||||
mat3x3(
|
|
||||||
-1, 0, 0,
|
|
||||||
0, -1, 0,
|
|
||||||
0, 0, -1
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
|
||||||
void main() {
|
|
||||||
vec2 size = vec2(imageSize(_CubemapImage).xy);
|
|
||||||
vec2 texCoord = (vec2(gl_GlobalInvocationID.xy) + vec2(0.5)) / size;
|
|
||||||
uint layerIndex = gl_GlobalInvocationID.z;
|
|
||||||
|
|
||||||
texCoord = texCoord * vec2(2.0) - vec2(1.0); // Map to range [-1, 1]
|
|
||||||
|
|
||||||
vec3 cubeCoord = MATRIX_2D_TO_CUBE[layerIndex] * vec3(texCoord, 1.0);
|
|
||||||
vec3 cubeDir = normalize(cubeCoord);
|
|
||||||
|
|
||||||
float theta = atan(cubeDir.y, cubeDir.x);
|
|
||||||
float phi = asin(cubeDir.z);
|
|
||||||
|
|
||||||
vec2 equirectCoord = vec2(theta * HALF_INV_PI, phi * INV_PI) + vec2(0.5);
|
|
||||||
|
|
||||||
vec4 irradiance = texture(sampler2D(_EquirectangularTexture, _EquirectangularSampler), equirectCoord);
|
|
||||||
imageStore(_CubemapImage, ivec3(gl_GlobalInvocationID.xyz), irradiance);
|
|
||||||
}
|
|
||||||
72
assets/shaders/equirect_to_cube.slang
Normal file
72
assets/shaders/equirect_to_cube.slang
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import "includes/math";
|
||||||
|
|
||||||
|
[[vk::binding(0, 0)]] SamplerState _EquirectangularSampler;
|
||||||
|
[[vk::binding(1, 0)]] Texture2D _EquirectangularTexture;
|
||||||
|
[[vk::binding(2, 0)]] WTexture2DArray _CubemapImage;
|
||||||
|
|
||||||
|
static const float3x3 MATRIX_2D_TO_CUBE[6] = {
|
||||||
|
// Positive X
|
||||||
|
{
|
||||||
|
0, 0, 1,
|
||||||
|
0, -1, 0,
|
||||||
|
-1, 0, 0,
|
||||||
|
},
|
||||||
|
// Negative X
|
||||||
|
{
|
||||||
|
0, 0, -1,
|
||||||
|
0, -1, 0,
|
||||||
|
1, 0, 0,
|
||||||
|
},
|
||||||
|
// Positive Y
|
||||||
|
{
|
||||||
|
1, 0, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
0, 1, 0,
|
||||||
|
},
|
||||||
|
// Negative Y
|
||||||
|
{
|
||||||
|
1, 0, 0,
|
||||||
|
0, 0, -1,
|
||||||
|
0, -1, 0,
|
||||||
|
},
|
||||||
|
// Positive Z
|
||||||
|
{
|
||||||
|
1, 0, 0,
|
||||||
|
0, -1, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
},
|
||||||
|
// Negative Z
|
||||||
|
{
|
||||||
|
-1, 0, 0,
|
||||||
|
0, -1, 0,
|
||||||
|
0, 0, -1,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
float2 cubemapSize() {
|
||||||
|
uint width, height, elements;
|
||||||
|
_CubemapImage.GetDimensions(width, height, elements);
|
||||||
|
return float2(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("compute")]
|
||||||
|
[numthreads(8, 8, 1)]
|
||||||
|
void main(uint3 threadID: SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
let size = cubemapSize();
|
||||||
|
var texCoord = (float2(threadID.xy) + 0.5) / size;
|
||||||
|
let layerIndex = threadID.z;
|
||||||
|
|
||||||
|
texCoord = 2.0 * texCoord - 1.0; // Map to range [-1, 1]
|
||||||
|
|
||||||
|
let cubeCoord = mul(MATRIX_2D_TO_CUBE[layerIndex], float3(texCoord, 1.0));
|
||||||
|
let cubeDir = normalize(cubeCoord);
|
||||||
|
|
||||||
|
let theta = atan2(cubeDir.y, cubeDir.x);
|
||||||
|
let phi = asin(cubeDir.z);
|
||||||
|
|
||||||
|
let equirectCoord = float2(theta * HALF_INV_PI, phi * INV_PI) + 0.5;
|
||||||
|
|
||||||
|
let irradiance = _EquirectangularTexture.SampleLevel(_EquirectangularSampler, equirectCoord, 0.0);
|
||||||
|
_CubemapImage.Store(threadID, irradiance);
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
in Varyings {
|
|
||||||
layout(location = 0) flat uint instance;
|
|
||||||
layout(location = 1) vec2 positionSSPX;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
#include "includes/gui_box_common.glsl"
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
float boxSDF(vec2 p, vec2 center, vec2 halfExtents, float borderRadius) {
|
|
||||||
vec2 q = abs(p - center) - halfExtents + borderRadius;
|
|
||||||
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - borderRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BOX _Boxes[var.instance]
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 halfExtentsPX = 0.5 * BOX.sizePX;
|
|
||||||
vec2 centerSSPX = BOX.positionSSPX + halfExtentsPX;
|
|
||||||
float borderHalfWidthPX = 0.5 * BOX.borderWidthPX;
|
|
||||||
|
|
||||||
float interiorSDF = boxSDF(var.positionSSPX, centerSSPX, halfExtentsPX, BOX.borderRadiusPX) + BOX.borderWidthPX;
|
|
||||||
float borderSDF = abs(interiorSDF - borderHalfWidthPX) - borderHalfWidthPX;
|
|
||||||
|
|
||||||
float interiorCoverage = clamp(-interiorSDF + 0.5, 0.0, 1.0) * BOX.backgroundColor.a;
|
|
||||||
float borderCoverage = clamp(-borderSDF + 0.5, 0.0, 1.0) * BOX.borderColor.a;
|
|
||||||
float totalCoverage = interiorCoverage + borderCoverage;
|
|
||||||
|
|
||||||
fragColor = vec4(
|
|
||||||
interiorCoverage * BOX.backgroundColor.rgb +
|
|
||||||
borderCoverage * BOX.borderColor.rgb,
|
|
||||||
totalCoverage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
65
assets/shaders/gui_box.slang
Normal file
65
assets/shaders/gui_box.slang
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import "includes/math";
|
||||||
|
import "includes/structs";
|
||||||
|
|
||||||
|
struct Varyings {
|
||||||
|
float4 positionCS : SV_Position;
|
||||||
|
nointerpolation uint instance;
|
||||||
|
float2 positionSSPX;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GuiBox {
|
||||||
|
float4 backgroundColor;
|
||||||
|
float4 borderColor;
|
||||||
|
float2 positionSSPX;
|
||||||
|
float2 sizePX;
|
||||||
|
float borderWidthPX;
|
||||||
|
float borderRadiusPX;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[vk::binding(0, 0)]] ConstantBuffer<GlobalUniforms> _Global;
|
||||||
|
[[vk::binding(1, 0)]] StructuredBuffer<GuiBox> _Boxes;
|
||||||
|
|
||||||
|
static const float2 VERTICES[4] = {
|
||||||
|
float2(0, 1),
|
||||||
|
float2(1, 1),
|
||||||
|
float2(0, 0),
|
||||||
|
float2(1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
Varyings vertexMain(uint vertexID : SV_VulkanVertexID, uint instanceID : SV_VulkanInstanceID) {
|
||||||
|
Varyings out;
|
||||||
|
|
||||||
|
let vertex = VERTICES[vertexID];
|
||||||
|
let box = _Boxes[instanceID];
|
||||||
|
|
||||||
|
let positionSSPX = vertex * box.sizePX + box.positionSSPX;
|
||||||
|
let positionCS = float4(mul(_Global.matrixSSPXtoCS, float3(positionSSPX, 1.0)), 0.0, 1.0);
|
||||||
|
|
||||||
|
out.positionCS = positionCS;
|
||||||
|
out.instance = instanceID;
|
||||||
|
out.positionSSPX = positionSSPX;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
float4 fragmentMain(Varyings frag) : SV_Target {
|
||||||
|
let box = _Boxes[frag.instance];
|
||||||
|
|
||||||
|
let halfExtentsPX = 0.5 * box.sizePX;
|
||||||
|
let centerSSPX = box.positionSSPX + halfExtentsPX;
|
||||||
|
let borderHalfWidthPX = 0.5 * box.borderWidthPX;
|
||||||
|
|
||||||
|
let interiorSDF = boxSDF(frag.positionSSPX, centerSSPX, halfExtentsPX, box.borderRadiusPX) + box.borderWidthPX;
|
||||||
|
float borderSDF = abs(interiorSDF - borderHalfWidthPX) - borderHalfWidthPX;
|
||||||
|
|
||||||
|
float interiorCoverage = saturate(-interiorSDF + 0.5) * box.backgroundColor.a;
|
||||||
|
float borderCoverage = saturate(-borderSDF + 0.5) * box.borderColor.a;
|
||||||
|
float totalCoverage = interiorCoverage + borderCoverage;
|
||||||
|
|
||||||
|
return float4(
|
||||||
|
interiorCoverage * box.backgroundColor.rgb +
|
||||||
|
borderCoverage * box.borderColor.rgb,
|
||||||
|
totalCoverage,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
out Varyings {
|
|
||||||
layout(location = 0) flat uint instance;
|
|
||||||
layout(location = 1) vec2 positionSSPX;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
#include "includes/gui_box_common.glsl"
|
|
||||||
|
|
||||||
const vec2 VERTICES[4] = vec2[](
|
|
||||||
vec2(0, 1),
|
|
||||||
vec2(1, 1),
|
|
||||||
vec2(0, 0),
|
|
||||||
vec2(1, 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
#define BOX _Boxes[gl_InstanceIndex]
|
|
||||||
#define VERTEX VERTICES[gl_VertexIndex]
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 positionSSPX = VERTEX * BOX.sizePX + BOX.positionSSPX;
|
|
||||||
vec4 positionCS = vec4(_Global.matrixSSPXtoCS * vec3(positionSSPX, 1.0), 0.0, 1.0);
|
|
||||||
|
|
||||||
gl_Position = positionCS;
|
|
||||||
var.instance = gl_InstanceIndex;
|
|
||||||
var.positionSSPX = positionSSPX;
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
in Varyings {
|
|
||||||
layout(location = 0) flat uint instance;
|
|
||||||
layout(location = 1) vec2 texCoord;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
#include "includes/gui_image_common.glsl"
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
#define IMAGE _Images[var.instance]
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 texel = texture(sampler2D(_Textures[uint(IMAGE.textureId)], _Sampler), var.texCoord);
|
|
||||||
fragColor = texel * IMAGE.tint;
|
|
||||||
}
|
|
||||||
53
assets/shaders/gui_image.slang
Normal file
53
assets/shaders/gui_image.slang
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import "includes/structs";
|
||||||
|
|
||||||
|
struct Varyings {
|
||||||
|
float4 positionCS : SV_Position;
|
||||||
|
nointerpolation uint instance;
|
||||||
|
float2 texCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GuiImage {
|
||||||
|
float4 tint;
|
||||||
|
float2 positionSSPX;
|
||||||
|
float2 sizePX;
|
||||||
|
float2 uvMin;
|
||||||
|
float2 uvMax;
|
||||||
|
uint16_t texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[vk::binding(0, 0)]] ConstantBuffer<GlobalUniforms> _Global;
|
||||||
|
[[vk::binding(1, 0)]] StructuredBuffer<GuiImage> _Images;
|
||||||
|
[[vk::binding(2, 0)]] SamplerState _Sampler;
|
||||||
|
[[vk::binding(3, 0)]] Texture2D _Textures[];
|
||||||
|
|
||||||
|
static const float2 VERTICES[4] = {
|
||||||
|
float2(0, 1),
|
||||||
|
float2(1, 1),
|
||||||
|
float2(0, 0),
|
||||||
|
float2(1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
Varyings vertexMain(uint vertexID : SV_VulkanVertexID, uint instanceID : SV_VulkanInstanceID) {
|
||||||
|
Varyings out;
|
||||||
|
|
||||||
|
let vertex = VERTICES[vertexID];
|
||||||
|
let image = _Images[instanceID];
|
||||||
|
|
||||||
|
let positionSSPX = vertex * image.sizePX + image.positionSSPX;
|
||||||
|
let positionCS = float4(mul(_Global.matrixSSPXtoCS, float3(positionSSPX, 1.0)), 0.0, 1.0);
|
||||||
|
|
||||||
|
let texCoord = vertex * (image.uvMax - image.uvMin) + image.uvMin;
|
||||||
|
|
||||||
|
out.positionCS = positionCS;
|
||||||
|
out.instance = instanceID;
|
||||||
|
out.texCoord = texCoord;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
float4 fragmentMain(Varyings frag) : SV_Target {
|
||||||
|
let image = _Images[frag.instance];
|
||||||
|
let texel = _Textures[image.texture].Sample(_Sampler, frag.texCoord);
|
||||||
|
return texel * image.tint;
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
out Varyings {
|
|
||||||
layout(location = 0) flat uint instance;
|
|
||||||
layout(location = 1) vec2 texCoord;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
#include "includes/gui_image_common.glsl"
|
|
||||||
|
|
||||||
const vec2 VERTICES[4] = vec2[](
|
|
||||||
vec2(0, 1),
|
|
||||||
vec2(1, 1),
|
|
||||||
vec2(0, 0),
|
|
||||||
vec2(1, 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
#define IMAGE _Images[gl_InstanceIndex]
|
|
||||||
#define VERTEX VERTICES[gl_VertexIndex]
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 positionSSPX = VERTEX * IMAGE.sizePX + IMAGE.positionSSPX;
|
|
||||||
vec4 positionCS = vec4(_Global.matrixSSPXtoCS * vec3(positionSSPX, 1.0), 0.0, 1.0);
|
|
||||||
|
|
||||||
vec2 texCoord = VERTEX * (IMAGE.uvMax - IMAGE.uvMin) + IMAGE.uvMin;
|
|
||||||
|
|
||||||
gl_Position = positionCS;
|
|
||||||
var.instance = gl_InstanceIndex;
|
|
||||||
var.texCoord = texCoord;
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
uint calcRootCode(float y1, float y2, float y3) {
|
|
||||||
uint i1 = floatBitsToUint(y1) >> 31U;
|
|
||||||
uint i2 = floatBitsToUint(y2) >> 30U;
|
|
||||||
uint i3 = floatBitsToUint(y3) >> 29U;
|
|
||||||
|
|
||||||
uint shift = (i2 & 2U) | (i1 & ~2U);
|
|
||||||
shift = (i3 & 4U) | (shift & ~4U);
|
|
||||||
|
|
||||||
return ((0x2E74U >> shift) & 0x0101U);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
fragColor = vec4(1.0, 0.0, 1.0, 1.0);
|
|
||||||
}
|
|
||||||
271
assets/shaders/gui_text.slang
Normal file
271
assets/shaders/gui_text.slang
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
import "includes/structs";
|
||||||
|
|
||||||
|
struct Varyings {
|
||||||
|
float4 positionCS : SV_Position;
|
||||||
|
nointerpolation uint instance;
|
||||||
|
float2 texCoordEM : U_TEXCOORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GuiText {
|
||||||
|
float4 color;
|
||||||
|
// Index into _GlyphData
|
||||||
|
uint glyph;
|
||||||
|
float2 bottomLeftSSPX;
|
||||||
|
float fontSizePX;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GlyphData {
|
||||||
|
float2 bottomLeftEM;
|
||||||
|
float2 sizeEM;
|
||||||
|
// Index into _BandData of the first vertical/horizontal band data.
|
||||||
|
// `bandPtr.x` refers to vertical bands and `bandPtr.y` refers to horizontal
|
||||||
|
// bands.
|
||||||
|
uint2 bandPtr;
|
||||||
|
// Number of vertical/horizontal bands. `bandCount.x` is the number of
|
||||||
|
// vertical bands and `bandCount.y` is the number of horizontal bands.
|
||||||
|
uint2 bandCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[vk::binding(0, 0)]] ConstantBuffer<GlobalUniforms> _Global;
|
||||||
|
[[vk::binding(1, 0)]] StructuredBuffer<GuiText> _Instances;
|
||||||
|
|
||||||
|
[[vk::binding(0, 1)]] StructuredBuffer<GlyphData> _GlyphData;
|
||||||
|
[[vk::binding(1, 1)]] StructuredBuffer<half2> _ControlPoints;
|
||||||
|
[[vk::binding(2, 1)]] StructuredBuffer<uint> _BandData;
|
||||||
|
|
||||||
|
static const float2 VERTICES[4] = {
|
||||||
|
float2(0, 1),
|
||||||
|
float2(1, 1),
|
||||||
|
float2(0, 0),
|
||||||
|
float2(1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Classify a quadratic Bézier curve based on the sign bits of the ray-relative
|
||||||
|
// Y coordinates. Returns `(t2 << 1) | t1` where `t1` and `t2` are bits
|
||||||
|
// signifying the root eligibility; `t1` possibly adding one to the winding
|
||||||
|
// number and `t2` possibly subtracting one from the winding number.
|
||||||
|
uint classifyQuadCurve(float y0, float y1, float y2)
|
||||||
|
{
|
||||||
|
// ┌─────┰────┬────┬────┰────┬────┐
|
||||||
|
// │class┃ y2 │ y1 │ y0 ┃ t2 │ t1 │
|
||||||
|
// ├─────╂────┼────┼────╂────┼────┤
|
||||||
|
// │ A ┃ 0 │ 0 │ 0 ┃ 0 │ 0 │
|
||||||
|
// │ B ┃ 0 │ 0 │ 1 ┃ 1 │ 0 │
|
||||||
|
// │ C ┃ 0 │ 1 │ 0 ┃ 1 │ 1 │
|
||||||
|
// │ D ┃ 0 │ 1 │ 1 ┃ 1 │ 0 │
|
||||||
|
// │ E ┃ 1 │ 0 │ 0 ┃ 0 │ 1 │
|
||||||
|
// │ F ┃ 1 │ 0 │ 1 ┃ 1 │ 1 │
|
||||||
|
// │ G ┃ 1 │ 1 │ 0 ┃ 0 │ 1 │
|
||||||
|
// │ H ┃ 1 │ 1 │ 1 ┃ 0 │ 0 │
|
||||||
|
// └─────┸────┴────┴────┸────┴────┘
|
||||||
|
//
|
||||||
|
// Lookup table constant:
|
||||||
|
// H G F E D C B A
|
||||||
|
// 00 01 11 01 10 11 10 00 = 0x1DB8
|
||||||
|
|
||||||
|
let sign0 = asuint(y0) >> 31;
|
||||||
|
let sign1 = asuint(y1) >> 31;
|
||||||
|
let sign2 = asuint(y2) >> 31;
|
||||||
|
|
||||||
|
let class = (sign0 << 0) | (sign1 << 1) | (sign2 << 2);
|
||||||
|
let shift = 2 * class;
|
||||||
|
|
||||||
|
return (0x2E74U >> shift) & 0b11U;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find intersections between a quadratic Bézier curve and `y = 0` line by
|
||||||
|
// solving a quadratic equation.
|
||||||
|
//
|
||||||
|
// Returns the X coordinates at witch the intersections occur for roots t1 and
|
||||||
|
// t2. Imaginary solutions to the quadratic equation will return double root at
|
||||||
|
// the global minimum.
|
||||||
|
float2 raycastQuadCurveHorizontally(float2 cp0, float2 cp1, float2 cp2)
|
||||||
|
{
|
||||||
|
static const float EPSILON = 1.0 / 65536.0;
|
||||||
|
|
||||||
|
// When solving for y = 0, we get a quadratic polynomial given by:
|
||||||
|
//
|
||||||
|
// at² - 2bt + c
|
||||||
|
//
|
||||||
|
// where:
|
||||||
|
// * a = cp0.y - 2 * cp1.y + cp2.y
|
||||||
|
// * b = cp0.y - cp1.y
|
||||||
|
// * c = cp0.y
|
||||||
|
//
|
||||||
|
// Let Δ = b² - ac
|
||||||
|
// Then:
|
||||||
|
//
|
||||||
|
// t1 = (b - sqrt(Δ)) / a
|
||||||
|
// t2 = (b + sqrt(Δ)) / a
|
||||||
|
|
||||||
|
let a = cp0 - 2.0 * cp1 + cp2;
|
||||||
|
let b = cp0 - cp1;
|
||||||
|
let c = cp0;
|
||||||
|
|
||||||
|
let Δ = sqrt(max(b.y * b.y - a.y * c.y, 0.0));
|
||||||
|
var t = (b.y + float2(-Δ, Δ)) / a.y;
|
||||||
|
|
||||||
|
// If nearly linear, solve -2bt + c = 0 directly
|
||||||
|
if (abs(a.y) < EPSILON) {
|
||||||
|
t = float2(0.5 * c.y / b.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (a.x * t - 2.0 * b.x) * t + c.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find intersections between a quadratic Bézier curve and `x = 0` line by
|
||||||
|
// solving a quadratic equation.
|
||||||
|
//
|
||||||
|
// Returns the Y coordinates at witch the intersections occur for roots t1 and
|
||||||
|
// t2. Imaginary solutions to the quadratic equation will return double root at
|
||||||
|
// the global minimum.
|
||||||
|
float2 raycastQuadCurveVertically(float2 cp0, float2 cp1, float2 cp2)
|
||||||
|
{
|
||||||
|
static const float EPSILON = 1.0 / 65536.0;
|
||||||
|
|
||||||
|
let a = cp0 - 2.0 * cp1 + cp2;
|
||||||
|
let b = cp0 - cp1;
|
||||||
|
let c = cp0;
|
||||||
|
|
||||||
|
let Δ = sqrt(max(b.x * b.x - a.x * c.x, 0.0));
|
||||||
|
var t = (b.x + float2(-Δ, Δ)) / a.x;
|
||||||
|
|
||||||
|
if (abs(a.x) < EPSILON) {
|
||||||
|
t = float2(0.5 * c.x / b.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (a.y * t - 2.0 * b.y) * t + c.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float calculateCoverage(float xCoverage, float yCoverage, float xWeight, float yWeight) {
|
||||||
|
static const float EPSILON = 1.0 / 65536.0;
|
||||||
|
|
||||||
|
var coverage = max(
|
||||||
|
abs(xCoverage * xWeight + yCoverage * yWeight) / max(xWeight + yWeight, EPSILON),
|
||||||
|
min(abs(xCoverage), abs(yCoverage)),
|
||||||
|
);
|
||||||
|
// non-zero fill rule
|
||||||
|
coverage = saturate(coverage);
|
||||||
|
|
||||||
|
return coverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
Varyings vertexMain(uint vertexID : SV_VulkanVertexID, uint instanceID : SV_VulkanInstanceID) {
|
||||||
|
Varyings out;
|
||||||
|
|
||||||
|
let vertex = VERTICES[vertexID];
|
||||||
|
let instance = _Instances[instanceID];
|
||||||
|
let glyph = _GlyphData[instance.glyph];
|
||||||
|
let normal = 2.0 * vertex - 1.0;
|
||||||
|
|
||||||
|
let sizePX = instance.fontSizePX * glyph.sizeEM;
|
||||||
|
|
||||||
|
let positionSSPX = vertex * sizePX + instance.bottomLeftSSPX;
|
||||||
|
let dilatedPositionSSPX = positionSSPX + 0.5 * normal;
|
||||||
|
let dilatedPositionCS = float4(mul(_Global.matrixSSPXtoCS, float3(positionSSPX, 1.0)), 0.0, 1.0);
|
||||||
|
|
||||||
|
let texCoordEM = vertex * glyph.sizeEM + glyph.bottomLeftEM;
|
||||||
|
let dilatedTexCoordEM = texCoordEM + (0.5 / instance.fontSizePX) * normal;
|
||||||
|
|
||||||
|
out.positionCS = dilatedPositionCS;
|
||||||
|
out.instance = instanceID;
|
||||||
|
out.texCoordEM = dilatedTexCoordEM;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
float4 fragmentMain(Varyings frag) : SV_Target {
|
||||||
|
let instance = _Instances[frag.instance];
|
||||||
|
let glyph = _GlyphData[instance.glyph];
|
||||||
|
|
||||||
|
let emPerPX = fwidth(frag.texCoordEM);
|
||||||
|
let pxPerEM = 1.0 / emPerPX;
|
||||||
|
|
||||||
|
let bandF = float2(glyph.bandCount) * (frag.texCoordEM - glyph.bottomLeftEM) / glyph.sizeEM;
|
||||||
|
let band = uint2(clamp(bandF, float2(0, 0), float2(glyph.bandCount - 1U)));
|
||||||
|
|
||||||
|
// --- HORIZONTAL BAND -----------------------------------------------------
|
||||||
|
|
||||||
|
var xCoverage = 0.0;
|
||||||
|
var xWeight = 0.0;
|
||||||
|
|
||||||
|
let horizontalBandPtr = glyph.bandPtr.y + band.y;
|
||||||
|
let horizontalBandData = _BandData[horizontalBandPtr];
|
||||||
|
let horizontalBandCurvePtr = horizontalBandPtr + (horizontalBandData >> 16);
|
||||||
|
let horizontalBandCurveCount = horizontalBandData & 0xFFFF;
|
||||||
|
|
||||||
|
for (uint horizontalBandCurveID = 0; horizontalBandCurveID < horizontalBandCurveCount; horizontalBandCurveID++) {
|
||||||
|
let cpPtr = _BandData[horizontalBandCurvePtr + horizontalBandCurveID];
|
||||||
|
let cp0 = float2(_ControlPoints[cpPtr]) - frag.texCoordEM;
|
||||||
|
let cp1 = float2(_ControlPoints[cpPtr + 1]) - frag.texCoordEM;
|
||||||
|
let cp2 = float2(_ControlPoints[cpPtr + 2]) - frag.texCoordEM;
|
||||||
|
|
||||||
|
if (max(max(cp0.x, cp1.x), cp2.x) * pxPerEM.x < -0.5) break;
|
||||||
|
|
||||||
|
let rootFlags = classifyQuadCurve(cp0.y, cp1.y, cp2.y);
|
||||||
|
if (rootFlags != 0U) {
|
||||||
|
|
||||||
|
let result = raycastQuadCurveHorizontally(cp0, cp1, cp2) * pxPerEM.x;
|
||||||
|
|
||||||
|
// consider t1 at X position result.x
|
||||||
|
if ((rootFlags & 0b01U) != 0U) {
|
||||||
|
xCoverage += saturate(result.x + 0.5);
|
||||||
|
xWeight = max(xWeight, saturate(1.0 - abs(result.x) * 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// consider t2 at X position result.y
|
||||||
|
if ((rootFlags & 0b10U) != 0U) {
|
||||||
|
xCoverage -= saturate(result.y + 0.5);
|
||||||
|
xWeight = max(xWeight, saturate(1.0 - abs(result.y) * 2.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- VERTICAL BAND -------------------------------------------------------
|
||||||
|
|
||||||
|
var yCoverage = 0.0;
|
||||||
|
var yWeight = 0.0;
|
||||||
|
|
||||||
|
let verticalBandPtr = glyph.bandPtr.x + band.x;
|
||||||
|
let verticalBandData = _BandData[verticalBandPtr];
|
||||||
|
let verticalBandCurvePtr = verticalBandPtr + (verticalBandData >> 16);
|
||||||
|
let verticalBandCurveCount = verticalBandData & 0xFFFF;
|
||||||
|
|
||||||
|
for (uint verticalBandCurveID = 0; verticalBandCurveID < verticalBandCurveCount; verticalBandCurveID++) {
|
||||||
|
let cpPtr = _BandData[verticalBandCurvePtr + verticalBandCurveID];
|
||||||
|
let cp0 = float2(_ControlPoints[cpPtr]) - frag.texCoordEM;
|
||||||
|
let cp1 = float2(_ControlPoints[cpPtr + 1]) - frag.texCoordEM;
|
||||||
|
let cp2 = float2(_ControlPoints[cpPtr + 2]) - frag.texCoordEM;
|
||||||
|
|
||||||
|
if (max(max(cp0.y, cp1.y), cp2.y) * pxPerEM.y < -0.5) break;
|
||||||
|
|
||||||
|
let rootFlags = classifyQuadCurve(cp0.x, cp1.x, cp2.x);
|
||||||
|
if (rootFlags != 0U) {
|
||||||
|
|
||||||
|
let result = raycastQuadCurveVertically(cp0, cp1, cp2) * pxPerEM.y;
|
||||||
|
|
||||||
|
// consider t1 at Y position result.x
|
||||||
|
if ((rootFlags & 0b01U) != 0U)
|
||||||
|
{
|
||||||
|
yCoverage -= saturate(result.x + 0.5);
|
||||||
|
yWeight = max(yWeight, saturate(1.0 - abs(result.x) * 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// consider t2 at Y position result.y
|
||||||
|
if ((rootFlags & 0b10U) != 0U)
|
||||||
|
{
|
||||||
|
yCoverage += saturate(result.y + 0.5);
|
||||||
|
yWeight = max(yWeight, saturate(1.0 - abs(result.y) * 2.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- RESOLVE -------------------------------------------------------------
|
||||||
|
|
||||||
|
let coverage = calculateCoverage(xCoverage, yCoverage, xWeight, yWeight) * instance.color.a;
|
||||||
|
return float4(
|
||||||
|
coverage * instance.color.rgb,
|
||||||
|
coverage,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
layout(location = 0) in vec4 positionOS_normalOS;
|
|
||||||
layout(location = 1) in vec4 texCoordEM_band_flags;
|
|
||||||
layout(location = 2) in vec4 jacobian;
|
|
||||||
layout(location = 3) in vec4 scale_offset;
|
|
||||||
layout(location = 4) in vec4 color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
layout(set = 0, binding = 0, scalar) uniform GlobalUniforms {
|
|
||||||
mat4 matrixWStoVS;
|
|
||||||
mat4 matrixVStoCS;
|
|
||||||
mat3x2 matrixSSPXtoCS;
|
|
||||||
vec3 ambientLight;
|
|
||||||
} _Global;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
struct Box {
|
|
||||||
vec4 backgroundColor;
|
|
||||||
vec4 borderColor;
|
|
||||||
vec2 positionSSPX;
|
|
||||||
vec2 sizePX;
|
|
||||||
float borderWidthPX;
|
|
||||||
float borderRadiusPX;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "global_uniforms.glsl"
|
|
||||||
|
|
||||||
layout(set = 0, binding = 1, scalar) readonly buffer Boxes {
|
|
||||||
Box _Boxes[];
|
|
||||||
};
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
struct Image {
|
|
||||||
vec4 tint;
|
|
||||||
vec2 positionSSPX;
|
|
||||||
vec2 sizePX;
|
|
||||||
vec2 uvMin;
|
|
||||||
vec2 uvMax;
|
|
||||||
uint16_t textureId;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "global_uniforms.glsl"
|
|
||||||
|
|
||||||
layout(set = 0, binding = 1, scalar) readonly buffer Images {
|
|
||||||
Image _Images[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(set = 0, binding = 2) uniform sampler _Sampler;
|
|
||||||
layout(set = 0, binding = 3) uniform texture2D _Textures[];
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
// --- SET 0 --- GLOBAL --------------------------------------------------------
|
|
||||||
|
|
||||||
struct PointLight {
|
|
||||||
vec3 positionWS;
|
|
||||||
vec3 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DirectionalLight {
|
|
||||||
vec3 directionWS;
|
|
||||||
vec3 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Material {
|
|
||||||
vec3 baseColor;
|
|
||||||
vec3 emissive;
|
|
||||||
float ior;
|
|
||||||
float metallic;
|
|
||||||
float normalScale;
|
|
||||||
float occlusionTextureStrength;
|
|
||||||
float roughness;
|
|
||||||
|
|
||||||
uint16_t baseColorTexture;
|
|
||||||
uint16_t emissiveTexture;
|
|
||||||
uint16_t normalTexture;
|
|
||||||
uint16_t occlusionRoughnessMetallicTexture;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ObjectUniforms {
|
|
||||||
mat4 matrixOStoWS;
|
|
||||||
mat4 matrixOStoWSNormal;
|
|
||||||
|
|
||||||
uint16_t material;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "global_uniforms.glsl"
|
|
||||||
|
|
||||||
layout(set = 0, binding = 1, scalar) readonly buffer PointLights {
|
|
||||||
uint count;
|
|
||||||
PointLight lights[];
|
|
||||||
} _PointLights;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 2, scalar) readonly buffer DirectionalLights {
|
|
||||||
uint count;
|
|
||||||
DirectionalLight lights[];
|
|
||||||
} _DirectionalLights;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 3, scalar) readonly buffer Materials {
|
|
||||||
Material _Materials[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(set = 0, binding = 4) uniform sampler _Sampler;
|
|
||||||
layout(set = 0, binding = 5) uniform texture2D _Textures[];
|
|
||||||
|
|
||||||
// --- SET 1 --- PER BATCH -----------------------------------------------------
|
|
||||||
|
|
||||||
layout(set = 1, binding = 0, scalar) readonly buffer ObjectsUniforms {
|
|
||||||
ObjectUniforms _Object[];
|
|
||||||
};
|
|
||||||
77
assets/shaders/includes/math.slang
Normal file
77
assets/shaders/includes/math.slang
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import "structs";
|
||||||
|
|
||||||
|
static const float INV_PI = 0.31830987;
|
||||||
|
static const float HALF_INV_PI = 0.15915494;
|
||||||
|
|
||||||
|
static const float3 F90 = float3(1.0);
|
||||||
|
|
||||||
|
float4 texture2DAA(Texture2D texture, SamplerState sampler, float2 texCoord) {
|
||||||
|
uint width, height;
|
||||||
|
texture.GetDimensions(width, height);
|
||||||
|
let size = float2(width, height);
|
||||||
|
|
||||||
|
var texCoordPX = texCoord * size;
|
||||||
|
let seam = floor(texCoordPX + 0.5);
|
||||||
|
|
||||||
|
texCoordPX = (texCoordPX - seam) / fwidth(texCoordPX) + seam;
|
||||||
|
texCoordPX = clamp(texCoordPX, seam - 0.5, seam + 0.5);
|
||||||
|
|
||||||
|
texCoord = texCoordPX / size;
|
||||||
|
return texture.Sample(sampler, texCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 fresnelSchlick(float dotVH, float3 f0) {
|
||||||
|
return lerp(f0, F90, pow(1.0 - dotVH, 5.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
float visibilityGGX(float dotNL, float dotNV, float alpha) {
|
||||||
|
let alphaSquared = alpha * alpha;
|
||||||
|
|
||||||
|
let vGGX = dotNL * sqrt(dotNV * dotNV * (1.0 - alphaSquared) + alphaSquared);
|
||||||
|
let lGGX = dotNV * sqrt(dotNL * dotNL * (1.0 - alphaSquared) + alphaSquared);
|
||||||
|
let GGX = vGGX + lGGX;
|
||||||
|
return select(GGX > 0.0, 0.5 / GGX, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float distributionGGX(float dotNH, float alpha) {
|
||||||
|
let alphaSquared = alpha * alpha;
|
||||||
|
let tmp = dotNH * dotNH * (alphaSquared - 1.0) + 1.0;
|
||||||
|
return alphaSquared * INV_PI / (tmp * tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 lightOutgoingRadiance(
|
||||||
|
float3 viewDirectionVS, float3 normalVS, float dotNV,
|
||||||
|
Surface surf, IncomingLight light,
|
||||||
|
) {
|
||||||
|
let halfVectorVS = normalize(light.directionVS + viewDirectionVS);
|
||||||
|
let dotVH = saturate(dot(viewDirectionVS, halfVectorVS));
|
||||||
|
let dotNH = saturate(dot(normalVS, halfVectorVS));
|
||||||
|
let dotNL = saturate(dot(normalVS, light.directionVS));
|
||||||
|
|
||||||
|
let fresnel = fresnelSchlick(dotVH, surf.f0);
|
||||||
|
let visibility = visibilityGGX(dotNL, dotNV, surf.alpha);
|
||||||
|
let distribution = distributionGGX(dotNH, surf.alpha);
|
||||||
|
|
||||||
|
let scatteredFactor = (1.0 - fresnel) * (1.0 - surf.metallic) * surf.baseColor * INV_PI;
|
||||||
|
let reflectedFactor = fresnel * visibility * distribution;
|
||||||
|
|
||||||
|
return (scatteredFactor + reflectedFactor) * light.radiance * dotNL;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 toneMapAcesNarkowicz(float3 color) {
|
||||||
|
static const float A = 2.51;
|
||||||
|
static const float B = 0.03;
|
||||||
|
static const float C = 2.43;
|
||||||
|
static const float D = 0.59;
|
||||||
|
static const float E = 0.14;
|
||||||
|
return saturate((color * (A * color + B)) / (color * (C * color + D) + E));
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 linearToSrgbColor(float3 color) {
|
||||||
|
return pow(color, 1.0 / 2.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
float boxSDF(float2 p, float2 center, float2 halfExtents, float borderRadius) {
|
||||||
|
let q = abs(p - center) - halfExtents + borderRadius;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - borderRadius;
|
||||||
|
}
|
||||||
82
assets/shaders/includes/structs.slang
Normal file
82
assets/shaders/includes/structs.slang
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
struct GlobalUniforms {
|
||||||
|
float4x4 matrixWStoVS;
|
||||||
|
float4x4 matrixVStoCS;
|
||||||
|
float2x3 matrixSSPXtoCS;
|
||||||
|
|
||||||
|
uint pointLightCount;
|
||||||
|
uint directionalLightCount;
|
||||||
|
float3 ambientLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PointLight {
|
||||||
|
float3 positionWS;
|
||||||
|
float3 color;
|
||||||
|
|
||||||
|
IncomingLight getIncoming(float4x4 matrixWStoVS, float3 positionVS) {
|
||||||
|
IncomingLight light;
|
||||||
|
|
||||||
|
let lightPositionVS = mul(matrixWStoVS, float4(positionWS, 1.0)).xyz;
|
||||||
|
let lightDirectionVS = normalize(lightPositionVS - positionVS);
|
||||||
|
let lightDistance = distance(positionVS, lightPositionVS);
|
||||||
|
let lightAttenuation = 1.0 / (lightDistance * lightDistance);
|
||||||
|
let incomingRadiance = color * lightAttenuation;
|
||||||
|
|
||||||
|
light.radiance = incomingRadiance;
|
||||||
|
light.directionVS = lightDirectionVS;
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DirectionalLight {
|
||||||
|
float3 directionWS;
|
||||||
|
float3 color;
|
||||||
|
|
||||||
|
IncomingLight getIncoming(float4x4 matrixWStoVS) {
|
||||||
|
IncomingLight light;
|
||||||
|
|
||||||
|
light.radiance = color;
|
||||||
|
light.directionVS = normalize(mul(matrixWStoVS, float4(-directionWS, 0.0)).xyz);
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Material {
|
||||||
|
float3 baseColor;
|
||||||
|
float3 emissive;
|
||||||
|
float ior;
|
||||||
|
float metallic;
|
||||||
|
float normalScale;
|
||||||
|
float occlusionTextureStrength;
|
||||||
|
float roughness;
|
||||||
|
|
||||||
|
uint16_t baseColorTexture;
|
||||||
|
uint16_t emissiveTexture;
|
||||||
|
uint16_t normalTexture;
|
||||||
|
uint16_t occlusionRoughnessMetallicTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObjectUniforms {
|
||||||
|
float4x4 matrixOStoWS;
|
||||||
|
float4x4 matrixOStoWSNormal;
|
||||||
|
|
||||||
|
uint16_t material;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Surface {
|
||||||
|
float3 baseColor;
|
||||||
|
float alpha;
|
||||||
|
float metallic;
|
||||||
|
float3 f0;
|
||||||
|
|
||||||
|
__init(float3 baseColor, float alpha, float metallic, float3 f0) {
|
||||||
|
this.baseColor = baseColor;
|
||||||
|
this.alpha = alpha;
|
||||||
|
this.metallic = metallic;
|
||||||
|
this.f0 = f0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IncomingLight {
|
||||||
|
float3 radiance;
|
||||||
|
float3 directionVS;
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
vec3 toneMapAcesNarkowicz(vec3 color) {
|
|
||||||
const float A = 2.51;
|
|
||||||
const float B = 0.03;
|
|
||||||
const float C = 2.43;
|
|
||||||
const float D = 0.59;
|
|
||||||
const float E = 0.14;
|
|
||||||
return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0);
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
in Varyings {
|
|
||||||
layout(location = 0) flat uint instance;
|
|
||||||
layout(location = 1) vec3 positionVS;
|
|
||||||
layout(location = 2) vec2 texCoord;
|
|
||||||
layout(location = 3) vec3 normalVS;
|
|
||||||
layout(location = 4) vec3 tangentVS;
|
|
||||||
layout(location = 5) vec3 bitangentVS;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
#include "includes/main_common.glsl"
|
|
||||||
#include "includes/tone_mapping.glsl"
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
const float INV_PI = 0.31830987;
|
|
||||||
const float IOR = 1.45;
|
|
||||||
const vec3 F90 = vec3(1.0);
|
|
||||||
|
|
||||||
vec3 fresnelSchlick(float dotVH, vec3 f0) {
|
|
||||||
return mix(f0, F90, pow(1.0 - dotVH, 5.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
float visibilityGGX(float dotNL, float dotNV, float alpha) {
|
|
||||||
float alphaSquared = alpha * alpha;
|
|
||||||
|
|
||||||
float vGGX = dotNL * sqrt(dotNV * dotNV * (1.0 - alphaSquared) + alphaSquared);
|
|
||||||
float lGGX = dotNV * sqrt(dotNL * dotNL * (1.0 - alphaSquared) + alphaSquared);
|
|
||||||
float GGX = vGGX + lGGX;
|
|
||||||
return mix(0.0, 0.5 / GGX, GGX > 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float distributionGGX(float dotNH, float alpha) {
|
|
||||||
float alphaSquared = alpha * alpha;
|
|
||||||
float tmp = dotNH * dotNH * (alphaSquared - 1.0) + 1.0;
|
|
||||||
return alphaSquared * INV_PI / (tmp * tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 lightOutgoingRadiance(
|
|
||||||
vec3 viewDirectionVS, vec3 normalVS, float dotNV,
|
|
||||||
vec3 baseColor, float alpha, float metallic, vec3 f0,
|
|
||||||
vec3 incomingRadiance, vec3 lightDirectionVS
|
|
||||||
) {
|
|
||||||
vec3 halfVectorVS = normalize(lightDirectionVS + viewDirectionVS);
|
|
||||||
float dotVH = clamp(dot(viewDirectionVS, halfVectorVS), 0.0, 1.0);
|
|
||||||
float dotNH = clamp(dot(normalVS, halfVectorVS), 0.0, 1.0);
|
|
||||||
float dotNL = clamp(dot(normalVS, lightDirectionVS), 0.0, 1.0);
|
|
||||||
|
|
||||||
vec3 fresnel = fresnelSchlick(dotVH, f0);
|
|
||||||
float visibility = visibilityGGX(dotNL, dotNV, alpha);
|
|
||||||
float distribution = distributionGGX(dotNH, alpha);
|
|
||||||
|
|
||||||
vec3 scatteredFactor = (1.0 - fresnel) * (1.0 - metallic) * baseColor * INV_PI;
|
|
||||||
vec3 reflectedFactor = fresnel * visibility * distribution;
|
|
||||||
|
|
||||||
return (scatteredFactor + reflectedFactor) * incomingRadiance * dotNL;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 texture2DAA(texture2D tex, vec2 texCoord) {
|
|
||||||
vec2 size = vec2(textureSize(sampler2D(tex, _Sampler), 0).xy);
|
|
||||||
vec2 texCoordPX = texCoord * size;
|
|
||||||
vec2 seam = floor(texCoordPX + vec2(0.5));
|
|
||||||
|
|
||||||
texCoordPX = (texCoordPX - seam) / fwidth(texCoordPX) + seam;
|
|
||||||
texCoordPX = clamp(texCoordPX, seam - 0.5, seam + 0.5);
|
|
||||||
|
|
||||||
texCoord = texCoordPX / size;
|
|
||||||
return texture(sampler2D(tex, _Sampler), texCoord);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define OBJECT _Object[var.instance]
|
|
||||||
#define MATERIAL _Materials[uint(OBJECT.material)]
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 baseColorTexel = texture2DAA(_Textures[uint(MATERIAL.baseColorTexture)], var.texCoord);
|
|
||||||
if (baseColorTexel.a < 0.5) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 occlusionRoughnessMetallicTexel = texture2DAA(_Textures[uint(MATERIAL.occlusionRoughnessMetallicTexture)], var.texCoord);
|
|
||||||
vec4 normalTexel = texture2DAA(_Textures[uint(MATERIAL.normalTexture)], var.texCoord);
|
|
||||||
vec4 emissiveTexel = texture2DAA(_Textures[uint(MATERIAL.emissiveTexture)], var.texCoord);
|
|
||||||
|
|
||||||
vec3 baseColor = MATERIAL.baseColor * baseColorTexel.rgb;
|
|
||||||
float occlusion = 1.0 + MATERIAL.occlusionTextureStrength * (occlusionRoughnessMetallicTexel.r - 1.0);
|
|
||||||
float roughness = MATERIAL.roughness * occlusionRoughnessMetallicTexel.g;
|
|
||||||
float metallic = MATERIAL.metallic * occlusionRoughnessMetallicTexel.b;
|
|
||||||
vec3 emissive = MATERIAL.emissive * emissiveTexel.rgb;
|
|
||||||
float ior = MATERIAL.ior;
|
|
||||||
|
|
||||||
vec3 tangentVS = normalize(var.tangentVS);
|
|
||||||
vec3 bitangentVS = normalize(var.bitangentVS);
|
|
||||||
mat3 matrixTStoVS = mat3(tangentVS, bitangentVS, var.normalVS);
|
|
||||||
vec3 normalTS = normalTexel.xyz;
|
|
||||||
vec3 normalVS = normalize(matrixTStoVS * normalTS);
|
|
||||||
|
|
||||||
vec3 positionVS = var.positionVS;
|
|
||||||
vec3 viewDirectionVS = normalize(-positionVS);
|
|
||||||
float dotNV = clamp(dot(normalVS, viewDirectionVS), 0.0, 1.0);
|
|
||||||
float alpha = roughness * roughness;
|
|
||||||
|
|
||||||
vec3 f0 = vec3(pow((ior - 1.0) / (ior + 1.0), 2.0));
|
|
||||||
f0 = mix(f0, baseColor, metallic);
|
|
||||||
|
|
||||||
vec3 outgoingRadiance = vec3(0.0);
|
|
||||||
|
|
||||||
for (uint i = 0; i < _PointLights.count; i++) {
|
|
||||||
PointLight light = _PointLights.lights[i];
|
|
||||||
|
|
||||||
vec3 lightPositionVS = (_Global.matrixWStoVS * vec4(light.positionWS, 1.0)).xyz;
|
|
||||||
vec3 lightDirectionVS = normalize(lightPositionVS - positionVS);
|
|
||||||
float lightDistance = distance(positionVS, lightPositionVS);
|
|
||||||
float lightAttenuation = 1.0 / (lightDistance * lightDistance);
|
|
||||||
vec3 incomingRadiance = light.color * lightAttenuation;
|
|
||||||
|
|
||||||
outgoingRadiance += lightOutgoingRadiance(
|
|
||||||
viewDirectionVS, normalVS, dotNV,
|
|
||||||
baseColor, alpha, metallic, f0,
|
|
||||||
incomingRadiance, lightDirectionVS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < _DirectionalLights.count; i++) {
|
|
||||||
DirectionalLight light = _DirectionalLights.lights[i];
|
|
||||||
|
|
||||||
vec3 lightDirectionVS = normalize((_Global.matrixWStoVS * vec4(-light.directionWS, 0.0)).xyz);
|
|
||||||
vec3 incomingRadiance = light.color;
|
|
||||||
|
|
||||||
outgoingRadiance += lightOutgoingRadiance(
|
|
||||||
viewDirectionVS, normalVS, dotNV,
|
|
||||||
baseColor, alpha, metallic, f0,
|
|
||||||
incomingRadiance, lightDirectionVS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
outgoingRadiance += _Global.ambientLight * baseColor * occlusion;
|
|
||||||
|
|
||||||
vec3 toneMappedLinearColor = toneMapAcesNarkowicz(outgoingRadiance);
|
|
||||||
vec3 toneMappedSrgbColor = pow(toneMappedLinearColor, vec3(1.0 / 2.2));
|
|
||||||
|
|
||||||
fragColor = vec4(toneMappedSrgbColor, 1.0);
|
|
||||||
}
|
|
||||||
122
assets/shaders/main.slang
Normal file
122
assets/shaders/main.slang
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import "includes/math";
|
||||||
|
import "includes/structs";
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
[[vk::location(0)]] float3 positionOS;
|
||||||
|
[[vk::location(1)]] float2 texCoord;
|
||||||
|
[[vk::location(2)]] float3 normalOS;
|
||||||
|
[[vk::location(3)]] float4 tangentOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Varyings {
|
||||||
|
float4 positionCS : SV_Position;
|
||||||
|
nointerpolation uint instance;
|
||||||
|
float3 positionVS;
|
||||||
|
float2 texCoord;
|
||||||
|
float3 normalVS;
|
||||||
|
float3 tangentVS;
|
||||||
|
float3 bitangentVS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[vk::binding(0, 0)]] ConstantBuffer<GlobalUniforms> _Global;
|
||||||
|
[[vk::binding(1, 0)]] StructuredBuffer<PointLight> _PointLights;
|
||||||
|
[[vk::binding(2, 0)]] StructuredBuffer<DirectionalLight> _DirectionalLights;
|
||||||
|
[[vk::binding(3, 0)]] StructuredBuffer<Material> _Materials;
|
||||||
|
[[vk::binding(4, 0)]] SamplerState _Sampler;
|
||||||
|
[[vk::binding(5, 0)]] Texture2D _Textures[];
|
||||||
|
|
||||||
|
[[vk::binding(0, 1)]] StructuredBuffer<ObjectUniforms> _Objects;
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
Varyings vertexMain(Vertex vert, uint instanceID : SV_VulkanInstanceID) {
|
||||||
|
Varyings out;
|
||||||
|
|
||||||
|
let object = _Objects[instanceID];
|
||||||
|
|
||||||
|
let positionWS = mul(object.matrixOStoWS, float4(vert.positionOS, 1.0)).xyz;
|
||||||
|
let positionVS = mul(_Global.matrixWStoVS, float4(positionWS, 1.0)).xyz;
|
||||||
|
let positionCS = mul(_Global.matrixVStoCS, float4(positionVS, 1.0));
|
||||||
|
|
||||||
|
let normalWS = normalize(mul(object.matrixOStoWSNormal, float4(vert.normalOS, 0.0)).xyz);
|
||||||
|
let normalVS = normalize(mul(_Global.matrixWStoVS, float4(normalWS, 0.0)).xyz);
|
||||||
|
|
||||||
|
let tangentWS = normalize(mul(object.matrixOStoWSNormal, float4(vert.tangentOS.xyz, 0.0)).xyz);
|
||||||
|
let tangentVS = normalize(mul(_Global.matrixWStoVS, float4(tangentWS, 0.0)).xyz);
|
||||||
|
|
||||||
|
let bitangentVS = vert.tangentOS.w * normalize(cross(normalVS, tangentVS));
|
||||||
|
|
||||||
|
out.positionCS = positionCS;
|
||||||
|
out.instance = instanceID;
|
||||||
|
out.positionVS = positionVS;
|
||||||
|
out.texCoord = vert.texCoord;
|
||||||
|
out.normalVS = normalVS;
|
||||||
|
out.tangentVS = tangentVS;
|
||||||
|
out.bitangentVS = bitangentVS;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
float4 fragmentMain(Varyings frag) : SV_Target {
|
||||||
|
let material = _Materials[_Objects[frag.instance].material];
|
||||||
|
|
||||||
|
let baseColorTexel = texture2DAA(_Textures[material.baseColorTexture], _Sampler, frag.texCoord);
|
||||||
|
if (baseColorTexel.a < 0.5) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
let occlusionRoughnessMetallicTexel = texture2DAA(_Textures[material.occlusionRoughnessMetallicTexture], _Sampler, frag.texCoord);
|
||||||
|
let normalTexel = texture2DAA(_Textures[material.normalTexture], _Sampler, frag.texCoord);
|
||||||
|
let emissiveTexel = texture2DAA(_Textures[material.emissiveTexture], _Sampler, frag.texCoord);
|
||||||
|
|
||||||
|
let baseColor = material.baseColor * baseColorTexel.rgb;
|
||||||
|
let occlusion = 1.0 + material.occlusionTextureStrength * (occlusionRoughnessMetallicTexel.r - 1.0);
|
||||||
|
let roughness = material.roughness * occlusionRoughnessMetallicTexel.g;
|
||||||
|
let metallic = material.metallic * occlusionRoughnessMetallicTexel.b;
|
||||||
|
let emissive = material.emissive * emissiveTexel.rgb;
|
||||||
|
let ior = material.ior;
|
||||||
|
|
||||||
|
let tangentVS = normalize(frag.tangentVS);
|
||||||
|
let bitangentVS = normalize(frag.bitangentVS);
|
||||||
|
let matrixTStoVS = transpose(float3x3(tangentVS, bitangentVS, frag.normalVS));
|
||||||
|
let normalTS = normalTexel.xyz;
|
||||||
|
let normalVS = normalize(mul(matrixTStoVS, normalTS));
|
||||||
|
|
||||||
|
let positionVS = frag.positionVS;
|
||||||
|
let viewDirectionVS = normalize(-positionVS);
|
||||||
|
let dotNV = saturate(dot(normalVS, viewDirectionVS));
|
||||||
|
let alpha = roughness * roughness;
|
||||||
|
|
||||||
|
var f0 = float3(pow((ior - 1.0) / (ior + 1.0), 2.0));
|
||||||
|
f0 = lerp(f0, baseColor, metallic);
|
||||||
|
|
||||||
|
let surface = Surface(baseColor, alpha, metallic, f0);
|
||||||
|
|
||||||
|
var outgoingRadiance = float3(0.0);
|
||||||
|
|
||||||
|
let pointLightCount = _Global.pointLightCount;
|
||||||
|
for (uint i = 0; i < pointLightCount; i++) {
|
||||||
|
let light = _PointLights[i].getIncoming(_Global.matrixWStoVS, positionVS);
|
||||||
|
|
||||||
|
outgoingRadiance += lightOutgoingRadiance(
|
||||||
|
viewDirectionVS, normalVS, dotNV,
|
||||||
|
surface, light,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let directionalLightCount = _Global.directionalLightCount;
|
||||||
|
for (uint i = 0; i < directionalLightCount; i++) {
|
||||||
|
let light = _DirectionalLights[i].getIncoming(_Global.matrixWStoVS);
|
||||||
|
|
||||||
|
outgoingRadiance += lightOutgoingRadiance(
|
||||||
|
viewDirectionVS, normalVS, dotNV,
|
||||||
|
surface, light,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
outgoingRadiance += _Global.ambientLight * baseColor * occlusion;
|
||||||
|
|
||||||
|
let toneMappedLinearColor = toneMapAcesNarkowicz(outgoingRadiance);
|
||||||
|
let toneMappedSrgbColor = linearToSrgbColor(toneMappedLinearColor);
|
||||||
|
|
||||||
|
return float4(toneMappedSrgbColor, 1.0);
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_nonuniform_qualifier : require
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 positionOS;
|
|
||||||
layout(location = 1) in vec2 texCoord;
|
|
||||||
layout(location = 2) in vec3 normalOS;
|
|
||||||
layout(location = 3) in vec4 tangentOS;
|
|
||||||
|
|
||||||
out Varyings {
|
|
||||||
layout(location = 0) flat uint instance;
|
|
||||||
layout(location = 1) vec3 positionVS;
|
|
||||||
layout(location = 2) vec2 texCoord;
|
|
||||||
layout(location = 3) vec3 normalVS;
|
|
||||||
layout(location = 4) vec3 tangentVS;
|
|
||||||
layout(location = 5) vec3 bitangentVS;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
#include "includes/main_common.glsl"
|
|
||||||
|
|
||||||
#define OBJECT _Object[gl_InstanceIndex]
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec3 positionWS = (OBJECT.matrixOStoWS * vec4(positionOS, 1.0)).xyz;
|
|
||||||
vec3 positionVS = (_Global.matrixWStoVS * vec4(positionWS, 1.0)).xyz;
|
|
||||||
vec4 positionCS = _Global.matrixVStoCS * vec4(positionVS, 1.0);
|
|
||||||
|
|
||||||
vec3 normalWS = normalize((OBJECT.matrixOStoWSNormal * vec4(normalOS, 0.0)).xyz);
|
|
||||||
vec3 normalVS = normalize((_Global.matrixWStoVS * vec4(normalWS, 0.0)).xyz);
|
|
||||||
|
|
||||||
vec3 tangentWS = normalize((OBJECT.matrixOStoWSNormal * vec4(tangentOS.xyz, 0.0)).xyz);
|
|
||||||
vec3 tangentVS = normalize((_Global.matrixWStoVS * vec4(tangentWS, 0.0)).xyz);
|
|
||||||
|
|
||||||
vec3 bitangentVS = tangentOS.w * normalize(cross(normalVS, tangentVS));
|
|
||||||
|
|
||||||
gl_Position = positionCS;
|
|
||||||
var.instance = gl_InstanceIndex;
|
|
||||||
var.positionVS = positionVS;
|
|
||||||
var.texCoord = texCoord;
|
|
||||||
var.normalVS = normalVS;
|
|
||||||
var.tangentVS = tangentVS;
|
|
||||||
var.bitangentVS = bitangentVS;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#version 460
|
|
||||||
|
|
||||||
in Varyings {
|
|
||||||
layout(location = 0) vec3 texCoord;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 1) uniform sampler _Sampler;
|
|
||||||
layout(set = 0, binding = 2) uniform textureCube _Texture;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
#include "includes/tone_mapping.glsl"
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 texel = texture(samplerCube(_Texture, _Sampler), var.texCoord);
|
|
||||||
vec3 outgoingRadiance = texel.rgb;
|
|
||||||
|
|
||||||
vec3 toneMappedLinearColor = toneMapAcesNarkowicz(outgoingRadiance);
|
|
||||||
vec3 toneMappedSrgbColor = pow(toneMappedLinearColor, vec3(1.0 / 2.2));
|
|
||||||
|
|
||||||
fragColor = vec4(toneMappedSrgbColor, 1.0);
|
|
||||||
}
|
|
||||||
38
assets/shaders/skybox.slang
Normal file
38
assets/shaders/skybox.slang
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import "includes/math";
|
||||||
|
import "includes/structs";
|
||||||
|
|
||||||
|
[[vk::binding(0, 0)]] ConstantBuffer<GlobalUniforms> _Global;
|
||||||
|
[[vk::binding(1, 0)]] SamplerState _Sampler;
|
||||||
|
[[vk::binding(2, 0)]] TextureCube _Texture;
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
[[vk::location(0)]] float3 directionWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Varyings {
|
||||||
|
float4 positionCS : SV_Position;
|
||||||
|
float3 texCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
Varyings vertexMain(Vertex vert) {
|
||||||
|
Varyings out;
|
||||||
|
|
||||||
|
let directionVS = mul(_Global.matrixWStoVS, float4(vert.directionWS, 0.0)).xyz;
|
||||||
|
let directionCS = mul(_Global.matrixVStoCS, float4(directionVS, 0.0));
|
||||||
|
|
||||||
|
out.positionCS = float4(directionCS.xy, 0.0, directionCS.w);
|
||||||
|
out.texCoord = -vert.directionWS;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
float4 fragmentMain(Varyings frag) : SV_Target {
|
||||||
|
let texel = _Texture.Sample(_Sampler, frag.texCoord);
|
||||||
|
let outgoingRadiance = texel.rgb;
|
||||||
|
|
||||||
|
let toneMappedLinearColor = toneMapAcesNarkowicz(outgoingRadiance);
|
||||||
|
let toneMappedSrgbColor = linearToSrgbColor(toneMappedLinearColor);
|
||||||
|
|
||||||
|
return float4(toneMappedSrgbColor, 1.0);
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#version 460
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 directionWS;
|
|
||||||
|
|
||||||
out Varyings {
|
|
||||||
layout(location = 0) vec3 texCoord;
|
|
||||||
} var;
|
|
||||||
|
|
||||||
#include "includes/global_uniforms.glsl"
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec3 directionVS = (_Global.matrixWStoVS * vec4(directionWS, 0.0)).xyz;
|
|
||||||
vec4 directionCS = _Global.matrixVStoCS * vec4(directionVS, 0.0);
|
|
||||||
|
|
||||||
gl_Position = vec4(directionCS.xy, 0.0, directionCS.w);
|
|
||||||
var.texCoord = -directionWS;
|
|
||||||
}
|
|
||||||
40
build.zig
40
build.zig
@@ -73,30 +73,36 @@ pub fn build(b: *std.Build) !void {
|
|||||||
run_step.dependOn(&run_cmd.step);
|
run_step.dependOn(&run_cmd.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isShaderExtension(extension: []const u8) bool {
|
|
||||||
return std.mem.eql(u8, extension, ".vert") or
|
|
||||||
std.mem.eql(u8, extension, ".frag") or
|
|
||||||
std.mem.eql(u8, extension, ".comp");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn addShaders(b: *std.Build, exe_mod: *std.Build.Module) !void {
|
fn addShaders(b: *std.Build, exe_mod: *std.Build.Module) !void {
|
||||||
var dir = try b.build_root.handle.openDir(b.graph.io, "assets/shaders", .{ .iterate = true });
|
var dir = try b.build_root.handle.openDir(b.graph.io, "assets/shaders", .{ .iterate = true });
|
||||||
defer dir.close(b.graph.io);
|
defer dir.close(b.graph.io);
|
||||||
|
|
||||||
var walker = try dir.walk(b.allocator);
|
var it = dir.iterateAssumeFirstIteration();
|
||||||
defer walker.deinit();
|
|
||||||
|
|
||||||
while (try walker.next(b.graph.io)) |entry| {
|
while (try it.next(b.graph.io)) |entry| {
|
||||||
if (entry.kind != .file) continue;
|
if (entry.kind != .file) continue;
|
||||||
if (!isShaderExtension(std.fs.path.extension(entry.path))) continue;
|
|
||||||
|
|
||||||
const glslc = b.addSystemCommand(&.{ "glslc", "-g", "--target-env=vulkan1.2" });
|
const stem = std.fs.path.stem(entry.name);
|
||||||
glslc.addFileArg(b.path(b.pathJoin(&.{ "assets/shaders", entry.path })));
|
const extension = std.fs.path.extension(entry.name);
|
||||||
glslc.addArg("-o");
|
if (!std.mem.eql(u8, extension, ".slang")) continue;
|
||||||
const spv = glslc.addOutputFileArg(b.fmt("{s}.spv", .{entry.basename}));
|
|
||||||
glslc.addArgs(&.{ "-MD", "-MF" });
|
|
||||||
_ = glslc.addDepFileOutputArg(b.fmt("{s}.d", .{entry.basename}));
|
|
||||||
|
|
||||||
exe_mod.addAnonymousImport(b.fmt("shaders/{s}", .{entry.path}), .{ .root_source_file = spv });
|
const slangc = b.addSystemCommand(&.{
|
||||||
|
"slangc",
|
||||||
|
"-g",
|
||||||
|
"-target",
|
||||||
|
"spirv",
|
||||||
|
"-fvk-use-scalar-layout",
|
||||||
|
"-matrix-layout-column-major",
|
||||||
|
});
|
||||||
|
|
||||||
|
slangc.addArg("-o");
|
||||||
|
const spv = slangc.addOutputFileArg(b.fmt("{s}.spv", .{stem}));
|
||||||
|
|
||||||
|
slangc.addArg("-depfile");
|
||||||
|
_ = slangc.addDepFileOutputArg(b.fmt("{s}.d", .{stem}));
|
||||||
|
|
||||||
|
slangc.addFileArg(b.path(b.pathJoin(&.{ "assets/shaders", entry.name })));
|
||||||
|
|
||||||
|
exe_mod.addAnonymousImport(b.fmt("shaders/{s}", .{stem}), .{ .root_source_file = spv });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/Game.zig
47
src/Game.zig
@@ -55,6 +55,14 @@ const chunk_descriptor_pool = 1024;
|
|||||||
|
|
||||||
const camera_near_plane = 0.1;
|
const camera_near_plane = 0.1;
|
||||||
|
|
||||||
|
const point_lights_data = [_]shaders.PointLight{};
|
||||||
|
const directional_lights_data = [_]shaders.DirectionalLight{
|
||||||
|
.{
|
||||||
|
.directionWS = .unit_nz,
|
||||||
|
.color = .init(0.3, 0.3, 0.3),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn init() !Game {
|
pub fn init() !Game {
|
||||||
const allocator_general = ctx.allocator_general;
|
const allocator_general = ctx.allocator_general;
|
||||||
const allocator_frame = ctx.allocator_frame;
|
const allocator_frame = ctx.allocator_frame;
|
||||||
@@ -158,13 +166,9 @@ pub fn init() !Game {
|
|||||||
errdefer engine.destroyPipelineLayout(pipeline_layout);
|
errdefer engine.destroyPipelineLayout(pipeline_layout);
|
||||||
engine.setObjectName(pipeline_layout, "PL Main", .{});
|
engine.setObjectName(pipeline_layout, "PL Main", .{});
|
||||||
|
|
||||||
const vertex_shader = try engine.createShaderModule(.{ .code = &shaders.main_vert_spv });
|
const shader = try engine.createShaderModule(.{ .code = &shaders.main_spv });
|
||||||
defer engine.destroyShaderModule(vertex_shader);
|
defer engine.destroyShaderModule(shader);
|
||||||
engine.setObjectName(vertex_shader, "SM main_vert", .{});
|
engine.setObjectName(shader, "SM main", .{});
|
||||||
|
|
||||||
const fragment_shader = try engine.createShaderModule(.{ .code = &shaders.main_frag_spv });
|
|
||||||
defer engine.destroyShaderModule(fragment_shader);
|
|
||||||
engine.setObjectName(fragment_shader, "SM main_frag", .{});
|
|
||||||
|
|
||||||
var vertex_buffer = try shaders.VertexBuffer.init(.{
|
var vertex_buffer = try shaders.VertexBuffer.init(.{
|
||||||
.usage = .vertex,
|
.usage = .vertex,
|
||||||
@@ -267,13 +271,13 @@ pub fn init() !Game {
|
|||||||
.stages = &.{
|
.stages = &.{
|
||||||
.{
|
.{
|
||||||
.stage = .{ .vertex_bit = true },
|
.stage = .{ .vertex_bit = true },
|
||||||
.module = vertex_shader,
|
.module = shader,
|
||||||
.name = "main",
|
.name = "vertexMain",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.stage = .{ .fragment_bit = true },
|
.stage = .{ .fragment_bit = true },
|
||||||
.module = fragment_shader,
|
.module = shader,
|
||||||
.name = "main",
|
.name = "fragmentMain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.vertex_input_state = .{
|
.vertex_input_state = .{
|
||||||
@@ -609,22 +613,8 @@ pub fn init() !Game {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const point_lights_data: []const shaders.PointLight = &.{};
|
try point_lights.write(.{ .elements = &point_lights_data });
|
||||||
try point_lights.write(.{
|
try directional_lights.write(.{ .elements = &directional_lights_data });
|
||||||
.header = @intCast(point_lights_data.len),
|
|
||||||
.elements = point_lights_data,
|
|
||||||
});
|
|
||||||
|
|
||||||
const directional_lights_data: []const shaders.DirectionalLight = &.{
|
|
||||||
.{
|
|
||||||
.directionWS = .unit_nz,
|
|
||||||
.color = .init(0.3, 0.3, 0.3),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
try directional_lights.write(.{
|
|
||||||
.header = @intCast(directional_lights_data.len),
|
|
||||||
.elements = directional_lights_data,
|
|
||||||
});
|
|
||||||
|
|
||||||
var skybox = try Skybox.load("skybox.hdr", 512, global_uniforms.buffer);
|
var skybox = try Skybox.load("skybox.hdr", 512, global_uniforms.buffer);
|
||||||
errdefer skybox.deinit();
|
errdefer skybox.deinit();
|
||||||
@@ -857,6 +847,9 @@ fn render(self: *Game) !void {
|
|||||||
.matrixWStoVS = matrix_ws_to_vs,
|
.matrixWStoVS = matrix_ws_to_vs,
|
||||||
.matrixVStoCS = matrix_vs_to_cs,
|
.matrixVStoCS = matrix_vs_to_cs,
|
||||||
.matrixSSPXtoCS = matrix_sspx_to_cs,
|
.matrixSSPXtoCS = matrix_sspx_to_cs,
|
||||||
|
|
||||||
|
.pointLightCount = point_lights_data.len,
|
||||||
|
.directionalLightCount = directional_lights_data.len,
|
||||||
.ambientLight = ambient_light,
|
.ambientLight = ambient_light,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -156,13 +156,9 @@ pub fn init() !*Gui {
|
|||||||
errdefer engine.destroyPipelineLayout(box_pipeline_layout);
|
errdefer engine.destroyPipelineLayout(box_pipeline_layout);
|
||||||
engine.setObjectName(box_pipeline_layout, "PL GUI Box", .{});
|
engine.setObjectName(box_pipeline_layout, "PL GUI Box", .{});
|
||||||
|
|
||||||
const box_vertex_shader = try engine.createShaderModule(.{ .code = &shaders.gui_box_vert_spv });
|
const box_shader = try engine.createShaderModule(.{ .code = &shaders.gui_box_spv });
|
||||||
defer engine.destroyShaderModule(box_vertex_shader);
|
defer engine.destroyShaderModule(box_shader);
|
||||||
engine.setObjectName(box_vertex_shader, "SM gui_box_vert", .{});
|
engine.setObjectName(box_shader, "SM gui_box", .{});
|
||||||
|
|
||||||
const box_fragment_shader = try engine.createShaderModule(.{ .code = &shaders.gui_box_frag_spv });
|
|
||||||
defer engine.destroyShaderModule(box_fragment_shader);
|
|
||||||
engine.setObjectName(box_fragment_shader, "SM gui_box_frag", .{});
|
|
||||||
|
|
||||||
var index_buffer = try shaders.IndexBuffer.init(.{
|
var index_buffer = try shaders.IndexBuffer.init(.{
|
||||||
.usage = .index,
|
.usage = .index,
|
||||||
@@ -179,13 +175,13 @@ pub fn init() !*Gui {
|
|||||||
.stages = &.{
|
.stages = &.{
|
||||||
.{
|
.{
|
||||||
.stage = .{ .vertex_bit = true },
|
.stage = .{ .vertex_bit = true },
|
||||||
.module = box_vertex_shader,
|
.module = box_shader,
|
||||||
.name = "main",
|
.name = "vertexMain",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.stage = .{ .fragment_bit = true },
|
.stage = .{ .fragment_bit = true },
|
||||||
.module = box_fragment_shader,
|
.module = box_shader,
|
||||||
.name = "main",
|
.name = "fragmentMain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.vertex_input_state = .{},
|
.vertex_input_state = .{},
|
||||||
|
|||||||
@@ -261,6 +261,26 @@ pub fn load(
|
|||||||
});
|
});
|
||||||
errdefer engine.destroyImageView(cubemap_image_view);
|
errdefer engine.destroyImageView(cubemap_image_view);
|
||||||
|
|
||||||
|
// NOTE Because slang cannot conceive of such a thing as RWTextureCube
|
||||||
|
// (equivalent to imageCube in GLSL, which docs claim exists, but it
|
||||||
|
// doesn't), we have to resort to RWTexture2DArray (WTexture2DArray to be
|
||||||
|
// specific) in the compute shader. We have to make another image view with
|
||||||
|
// appropriate view_type to pass validation.
|
||||||
|
|
||||||
|
const cubemap_image_view_2d_array = try engine.createImageView(.{
|
||||||
|
.image = cubemap_image,
|
||||||
|
.view_type = .@"2d_array",
|
||||||
|
.format = .r16g16b16a16_sfloat,
|
||||||
|
.subresource_range = .{
|
||||||
|
.aspect_mask = .{ .color_bit = true },
|
||||||
|
.base_mip_level = 0,
|
||||||
|
.level_count = 1,
|
||||||
|
.base_array_layer = 0,
|
||||||
|
.layer_count = 6,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defer engine.destroyImageView(cubemap_image_view_2d_array);
|
||||||
|
|
||||||
// --- PIPELINE AND DESCRIPTORS --------------------------------------------
|
// --- PIPELINE AND DESCRIPTORS --------------------------------------------
|
||||||
|
|
||||||
const sampler = try engine.createSampler(.{
|
const sampler = try engine.createSampler(.{
|
||||||
@@ -312,7 +332,7 @@ pub fn load(
|
|||||||
});
|
});
|
||||||
defer engine.destroyPipelineLayout(compute_pipeline_layout);
|
defer engine.destroyPipelineLayout(compute_pipeline_layout);
|
||||||
|
|
||||||
const compute_shader = try engine.createShaderModule(.{ .code = &shaders.equirect_to_cube_comp_spv });
|
const compute_shader = try engine.createShaderModule(.{ .code = &shaders.equirect_to_cube_spv });
|
||||||
defer engine.destroyShaderModule(compute_shader);
|
defer engine.destroyShaderModule(compute_shader);
|
||||||
|
|
||||||
var compute_pipeline: vk.Pipeline = undefined;
|
var compute_pipeline: vk.Pipeline = undefined;
|
||||||
@@ -380,7 +400,7 @@ pub fn load(
|
|||||||
.image = &.{
|
.image = &.{
|
||||||
.{
|
.{
|
||||||
.sampler = .null_handle,
|
.sampler = .null_handle,
|
||||||
.image_view = cubemap_image_view,
|
.image_view = cubemap_image_view_2d_array,
|
||||||
.image_layout = .general,
|
.image_layout = .general,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -622,23 +642,20 @@ pub fn load(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const vertex_shader = try engine.createShaderModule(.{ .code = &shaders.skybox_vert_spv });
|
const shader = try engine.createShaderModule(.{ .code = &shaders.skybox_spv });
|
||||||
defer engine.destroyShaderModule(vertex_shader);
|
defer engine.destroyShaderModule(shader);
|
||||||
|
|
||||||
const fragment_shader = try engine.createShaderModule(.{ .code = &shaders.skybox_frag_spv });
|
|
||||||
defer engine.destroyShaderModule(fragment_shader);
|
|
||||||
|
|
||||||
const pipeline = try engine.createGraphicsPipeline(.{
|
const pipeline = try engine.createGraphicsPipeline(.{
|
||||||
.stages = &.{
|
.stages = &.{
|
||||||
.{
|
.{
|
||||||
.stage = .{ .vertex_bit = true },
|
.stage = .{ .vertex_bit = true },
|
||||||
.module = vertex_shader,
|
.module = shader,
|
||||||
.name = "main",
|
.name = "vertexMain",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.stage = .{ .fragment_bit = true },
|
.stage = .{ .fragment_bit = true },
|
||||||
.module = fragment_shader,
|
.module = shader,
|
||||||
.name = "main",
|
.name = "fragmentMain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.vertex_input_state = .{
|
.vertex_input_state = .{
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ const Textures = @import("engine/Textures.zig");
|
|||||||
pub const VertexBuffer = GenericBuffer(void, Vertex);
|
pub const VertexBuffer = GenericBuffer(void, Vertex);
|
||||||
pub const IndexBuffer = GenericBuffer(void, Index);
|
pub const IndexBuffer = GenericBuffer(void, Index);
|
||||||
pub const GlobalUniformsBuffer = GenericBuffer(GlobalUniforms, void);
|
pub const GlobalUniformsBuffer = GenericBuffer(GlobalUniforms, void);
|
||||||
pub const PointLightBuffer = GenericBuffer(u32, PointLight);
|
pub const PointLightBuffer = GenericBuffer(void, PointLight);
|
||||||
pub const DirectionalLightBuffer = GenericBuffer(u32, DirectionalLight);
|
pub const DirectionalLightBuffer = GenericBuffer(void, DirectionalLight);
|
||||||
pub const MaterialBuffer = GenericBuffer(void, Material);
|
pub const MaterialBuffer = GenericBuffer(void, Material);
|
||||||
pub const ObjectUniformsBuffer = GenericBuffer(void, ObjectUniforms);
|
pub const ObjectUniformsBuffer = GenericBuffer(void, ObjectUniforms);
|
||||||
|
|
||||||
@@ -47,6 +47,9 @@ pub const GlobalUniforms = extern struct {
|
|||||||
matrixWStoVS: vm.Matrix4x4,
|
matrixWStoVS: vm.Matrix4x4,
|
||||||
matrixVStoCS: vm.Matrix4x4,
|
matrixVStoCS: vm.Matrix4x4,
|
||||||
matrixSSPXtoCS: vm.Matrix3x2,
|
matrixSSPXtoCS: vm.Matrix3x2,
|
||||||
|
|
||||||
|
pointLightCount: u32,
|
||||||
|
directionalLightCount: u32,
|
||||||
ambientLight: vm.Vector3,
|
ambientLight: vm.Vector3,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,10 +99,7 @@ pub const ObjectUniforms = extern struct {
|
|||||||
material: Materials.Id,
|
material: Materials.Id,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const equirect_to_cube_comp_spv align(4) = @embedFile("shaders/equirect_to_cube.comp").*;
|
pub const equirect_to_cube_spv align(4) = @embedFile("shaders/equirect_to_cube").*;
|
||||||
pub const gui_box_vert_spv align(4) = @embedFile("shaders/gui_box.vert").*;
|
pub const gui_box_spv align(4) = @embedFile("shaders/gui_box").*;
|
||||||
pub const gui_box_frag_spv align(4) = @embedFile("shaders/gui_box.frag").*;
|
pub const main_spv align(4) = @embedFile("shaders/main").*;
|
||||||
pub const main_vert_spv align(4) = @embedFile("shaders/main.vert").*;
|
pub const skybox_spv align(4) = @embedFile("shaders/skybox").*;
|
||||||
pub const main_frag_spv align(4) = @embedFile("shaders/main.frag").*;
|
|
||||||
pub const skybox_vert_spv align(4) = @embedFile("shaders/skybox.vert").*;
|
|
||||||
pub const skybox_frag_spv align(4) = @embedFile("shaders/skybox.frag").*;
|
|
||||||
|
|||||||
Reference in New Issue
Block a user