Commit for 2021.05.16.7z
This commit is contained in:
parent
7670c8c3af
commit
e7da8db000
16
Makefile
16
Makefile
@ -24,8 +24,11 @@ LIB_NAME += uf
|
||||
EXT_LIB_NAME += ext
|
||||
|
||||
#VULKAN_SDK_PATH += /c/VulkanSDK/1.2.154.0/
|
||||
VULKAN_SDK_PATH += /c/VulkanSDK/1.2.162.0/
|
||||
GLSL_VALIDATOR += $(VULKAN_SDK_PATH)/Bin32/glslangValidator
|
||||
#VULKAN_SDK_PATH += /c/VulkanSDK/1.2.162.0/
|
||||
VULKAN_SDK_PATH += /c/VulkanSDK/1.2.176.1/
|
||||
#GLSL_VALIDATOR += $(VULKAN_SDK_PATH)/Bin32/glslangValidator
|
||||
GLSL_VALIDATOR += $(VULKAN_SDK_PATH)/Bin32/glslc
|
||||
SPV_OPTIMIZER += $(VULKAN_SDK_PATH)/Bin32/spirv-opt
|
||||
# Base Engine's DLL
|
||||
INC_DIR += $(ENGINE_INC_DIR)/$(ARCH)/$(PREFIX)
|
||||
DEPS +=
|
||||
@ -39,7 +42,7 @@ INCS += -I$(ENGINE_INC_DIR) -I$(INC_DIR) -I$(VULKAN_SDK_PATH)/include -I/mi
|
||||
LIBS += -L$(ENGINE_LIB_DIR) -L$(LIB_DIR) -L$(LIB_DIR)/$(PREFIX) -L$(VULKAN_SDK_PATH)/Lib
|
||||
|
||||
ifneq (,$(findstring win64,$(ARCH)))
|
||||
REQ_DEPS += vulkan json:nlohmann png openal ogg freetype ncurses curl luajit bullet meshoptimizer xatlas # draco discord
|
||||
REQ_DEPS += vulkan json:nlohmann png openal ogg freetype ncurses curl luajit bullet meshoptimizer xatlas openvr # draco discord
|
||||
FLAGS +=
|
||||
DEPS += -lgdi32
|
||||
else ifneq (,$(findstring dreamcast,$(ARCH)))
|
||||
@ -240,7 +243,9 @@ $(TARGET): $(OBJS)
|
||||
endif
|
||||
|
||||
%.spv: %.glsl
|
||||
$(GLSL_VALIDATOR) -V -o $@ $<
|
||||
# $(GLSL_VALIDATOR) -V -o $@ $<
|
||||
$(GLSL_VALIDATOR) -std=450 -o $@ $<
|
||||
$(SPV_OPTIMIZER) --preserve-bindings --preserve-spec-constants -O $@ -o $@
|
||||
|
||||
ifneq (,$(findstring dreamcast,$(ARCH)))
|
||||
clean:
|
||||
@ -283,4 +288,7 @@ clean-exe:
|
||||
-rm $(EX_DLL)
|
||||
-rm $(EXT_EX_DLL)
|
||||
-rm $(TARGET)
|
||||
-rm $(TARGET_SHADERS)
|
||||
|
||||
clean-shaders:
|
||||
-rm $(TARGET_SHADERS)
|
||||
@ -1,7 +1,11 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define UF_DEFERRED_SAMPLING 0
|
||||
#define UF_CAN_DISCARD 1
|
||||
#define TEXTURES 1
|
||||
#define MAX_TEXTURES TEXTURES
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
layout (binding = 1) uniform sampler2D samplerTexture;
|
||||
|
||||
@ -12,36 +16,26 @@ layout (location = 3) in vec3 inPosition;
|
||||
|
||||
layout (location = 0) out uvec2 outId;
|
||||
layout (location = 1) out vec2 outNormals;
|
||||
#if UF_DEFERRED_SAMPLING
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (location = 2) out vec2 outUvs;
|
||||
#else
|
||||
layout (location = 2) out vec4 outAlbedo;
|
||||
#endif
|
||||
|
||||
vec2 encodeNormals( vec3 n ) {
|
||||
float p = sqrt(n.z*8+8);
|
||||
return n.xy/p + 0.5;
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
vec2 dx_vtc = dFdx(uv);
|
||||
vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
float mip = mipLevel(inUv.xy);
|
||||
vec2 uv = inUv.xy;
|
||||
vec4 C = vec4(1, 1, 1, 1);
|
||||
vec3 P = inPosition;
|
||||
vec3 N = inNormal;
|
||||
#if UF_DEFERRED_SAMPLING
|
||||
#if DEFERRED_SAMPLING
|
||||
outUvs = wrap(inUv.xy);
|
||||
vec4 outAlbedo = vec4(0,0,0,0);
|
||||
#endif
|
||||
#if !UF_DEFERRED_SAMPLING || UF_CAN_DISCARD
|
||||
#if !DEFERRED_SAMPLING || CAN_DISCARD
|
||||
C = textureLod( samplerTexture, uv, mip );
|
||||
#endif
|
||||
#if !UF_DEFERRED_SAMPLING
|
||||
#if !DEFERRED_SAMPLING
|
||||
outAlbedo = C * inColor;
|
||||
#endif
|
||||
outNormals = encodeNormals( N );
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
|
||||
56
bin/data/shaders/common/fog.h
Normal file
56
bin/data/shaders/common/fog.h
Normal file
@ -0,0 +1,56 @@
|
||||
// Perlin Fog
|
||||
void fog( in Ray ray, inout vec3 i, float scale ) {
|
||||
if ( ubo.fog.stepScale <= 0 || ubo.fog.range.x == 0 || ubo.fog.range.y == 0 ) return;
|
||||
#if FOG_RAY_MARCH
|
||||
const float range = ubo.fog.range.y;
|
||||
const vec3 boundsMin = vec3(-range,-range,-range) + ray.origin;
|
||||
const vec3 boundsMax = vec3(range,range,range) + ray.origin;
|
||||
const int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
|
||||
|
||||
const vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, ray );
|
||||
const float dstToBox = rayBoxInfo.x;
|
||||
const float dstInsideBox = rayBoxInfo.y;
|
||||
const float depth = surface.position.eye.z;
|
||||
|
||||
const float aperture = PI * 0.5;
|
||||
const float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
|
||||
// march
|
||||
if ( 0 <= dstInsideBox && dstToBox <= depth ) {
|
||||
float stepSize = dstInsideBox / numSteps;
|
||||
float dstLimit = min( depth - dstToBox, dstInsideBox );
|
||||
float totalDensity = 0;
|
||||
float transmittance = 1;
|
||||
float lightFactor = scale;
|
||||
float coneDiameter = coneCoefficient * ray.distance;
|
||||
float level = aperture > 0 ? log2( coneDiameter ) : 0;
|
||||
float density = 0;
|
||||
vec3 uvw;
|
||||
ray.distance = dstToBox;
|
||||
while ( ray.distance < dstLimit ) {
|
||||
ray.distance += stepSize;
|
||||
ray.position = ray.origin + ray.direction * ray.distance;
|
||||
coneDiameter = coneCoefficient * ray.distance;
|
||||
level = aperture > 0 ? log2( coneDiameter ) : 0;
|
||||
uvw = ray.position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
|
||||
density = max(0, textureLod(samplerNoise, uvw, level).r - ubo.fog.densityThreshold) * ubo.fog.densityMultiplier;
|
||||
if ( density > 0 ) {
|
||||
density = exp(-density * stepSize * ubo.fog.absorbtion);
|
||||
transmittance *= density;
|
||||
lightFactor *= density;
|
||||
if ( transmittance < 0.1 ) break;
|
||||
}
|
||||
}
|
||||
i.rgb = mix(ubo.fog.color.rgb, i.rgb, transmittance );
|
||||
}
|
||||
#endif
|
||||
#if FOG_BASIC
|
||||
const vec3 color = ubo.fog.color.rgb;
|
||||
const float inner = ubo.fog.range.x;
|
||||
const float outer = ubo.fog.range.y * scale;
|
||||
const float distance = length(-surface.position.eye);
|
||||
const float factor = clamp( (distance - inner) / (outer - inner), 0.0, 1.0 );
|
||||
|
||||
i.rgb = mix(i.rgb, color, factor);
|
||||
#endif
|
||||
}
|
||||
73
bin/data/shaders/common/functions.h
Normal file
73
bin/data/shaders/common/functions.h
Normal file
@ -0,0 +1,73 @@
|
||||
// Helper Functions
|
||||
float random(vec3 seed, int i){ return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453); }
|
||||
float rand2(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453); }
|
||||
float rand3(vec3 co){ return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453); }
|
||||
float max3( vec3 v ) { return max(max(v.x, v.y), v.z); }
|
||||
float min3( vec3 v ) { return min(min(v.x, v.y), v.z); }
|
||||
uint biasedRound( float x, float bias ) { return uint( ( x < bias ) ? floor(x) : ceil(x)); }
|
||||
float wrap( float i ) { return fract(i); }
|
||||
vec2 wrap( vec2 uv ) { return vec2( wrap( uv.x ), wrap( uv.y ) ); }
|
||||
vec3 orthogonal(vec3 u){
|
||||
u = normalize(u);
|
||||
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
|
||||
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
|
||||
}
|
||||
void whitenoise(inout vec3 color, const vec4 parameters) {
|
||||
const float flicker = parameters.x;
|
||||
const float pieces = parameters.y;
|
||||
const float blend = parameters.z;
|
||||
const float time = parameters.w;
|
||||
if ( blend < 0.0001 ) return;
|
||||
const float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
|
||||
const float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
|
||||
color = mix( color, vec3(whiteNoise), blend );
|
||||
}
|
||||
vec3 decodeNormals( vec2 enc ) {
|
||||
const vec2 ang = enc*2-1;
|
||||
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
|
||||
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
|
||||
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
|
||||
}
|
||||
vec2 encodeNormals( vec3 n ) {
|
||||
// float p = sqrt(n.z*8+8);
|
||||
// return n.xy/p + 0.5;
|
||||
return (vec2(atan(n.y,n.x)/PI, n.z)+1.0)*0.5;
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
const vec2 dx_vtc = dFdx(uv);
|
||||
const vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
bool validTextureIndex( int textureIndex ) {
|
||||
return 0 <= textureIndex && textureIndex < MAX_TEXTURES;
|
||||
}
|
||||
vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, in Ray ray ) {
|
||||
const vec3 t0 = (boundsMin - ray.origin) / ray.direction;
|
||||
const vec3 t1 = (boundsMax - ray.origin) / ray.direction;
|
||||
const vec3 tmin = min(t0, t1);
|
||||
const vec3 tmax = max(t0, t1);
|
||||
const float tStart = max(0, max( max(tmin.x, tmin.y), tmin.z ));
|
||||
const float tEnd = max(0, min( tmax.x, min(tmax.y, tmax.z) ) - tStart);
|
||||
return vec2(tStart, tEnd);
|
||||
}
|
||||
vec4 resolve( subpassInputMS t, const uint samples ) {
|
||||
vec4 resolved = vec4(0);
|
||||
for ( int i = 0; i < samples; ++i ) resolved += subpassLoad(t, i);
|
||||
resolved /= vec4(samples);
|
||||
return resolved;
|
||||
}
|
||||
uvec4 resolve( usubpassInputMS t, const uint samples ) {
|
||||
uvec4 resolved = uvec4(0);
|
||||
for ( int i = 0; i < samples; ++i ) resolved += subpassLoad(t, i);
|
||||
resolved /= uvec4(samples);
|
||||
return resolved;
|
||||
}
|
||||
vec4 resolve( sampler2DMS t, ivec2 uv ) {
|
||||
vec4 resolved = vec4(0);
|
||||
int samples = textureSamples(t);
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
resolved += texelFetch(t, uv, i);
|
||||
}
|
||||
resolved /= float(samples);
|
||||
return resolved;
|
||||
}
|
||||
47
bin/data/shaders/common/macros.h
Normal file
47
bin/data/shaders/common/macros.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef MULTISAMPLING
|
||||
#define MULTISAMPLING 1
|
||||
#endif
|
||||
#ifndef DEFERRED_SAMPLING
|
||||
#define DEFERRED_SAMPLING 1
|
||||
#endif
|
||||
#ifndef CAN_DISCARD
|
||||
#define CAN_DISCARD 1
|
||||
#endif
|
||||
#ifndef USE_LIGHTMAP
|
||||
#define USE_LIGHTMAP 1
|
||||
#endif
|
||||
#if VXGI
|
||||
#define VXGI_NDC 1
|
||||
#define VXGI_SHADOWS 0
|
||||
#endif
|
||||
#ifndef MAX_TEXTURES
|
||||
#define MAX_TEXTURES TEXTURES
|
||||
#endif
|
||||
#ifndef FOG
|
||||
#define FOG 1
|
||||
#endif
|
||||
#ifndef FOG_RAY_MARCH
|
||||
#define FOG_RAY_MARCH 1
|
||||
#endif
|
||||
|
||||
#ifndef WHITENOISE
|
||||
#define WHITENOISE 1
|
||||
#endif
|
||||
#ifndef GAMMA_CORRECT
|
||||
#define GAMMA_CORRECT 1
|
||||
#endif
|
||||
#ifndef TONE_MAP
|
||||
#define TONE_MAP 1
|
||||
#endif
|
||||
#ifndef LAMBERT
|
||||
#define LAMBERT 0
|
||||
#endif
|
||||
#ifndef PBR
|
||||
#define PBR 1
|
||||
#endif
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
const float EPSILON = 0.00001;
|
||||
const float SQRT2 = 1.41421356237;
|
||||
|
||||
const float LIGHT_POWER_CUTOFF = 0.0005;
|
||||
54
bin/data/shaders/common/pbr.h
Normal file
54
bin/data/shaders/common/pbr.h
Normal file
@ -0,0 +1,54 @@
|
||||
// PBR
|
||||
float shadowFactor( const Light light, float def );
|
||||
float ndfGGX(float cosLh, float roughness) {
|
||||
const float alpha = roughness * roughness;
|
||||
const float alphaSq = alpha * alpha;
|
||||
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
|
||||
return alphaSq / (PI * denom * denom);
|
||||
}
|
||||
float gaSchlickG1(float cosTheta, float k) { return cosTheta / (cosTheta * (1.0 - k) + k); }
|
||||
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
|
||||
const float r = roughness + 1.0;
|
||||
const float k = (r * r) / 8.0;
|
||||
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
|
||||
}
|
||||
vec3 fresnelSchlick(vec3 F0, float cosTheta) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); }
|
||||
void pbr() {
|
||||
const float Rs = 4.0; // specular lighting looks gross without this
|
||||
const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic);
|
||||
const vec3 Lo = normalize( -surface.position.eye );
|
||||
const float cosLo = max(0.0, dot(surface.normal.eye, Lo));
|
||||
|
||||
for ( uint i = 0; i < ubo.lights; ++i ) {
|
||||
const Light light = lights[i];
|
||||
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
|
||||
const vec3 Lp = light.position;
|
||||
const vec3 Liu = vec3(ubo.matrices.view[surface.pass] * vec4(light.position, 1)) - surface.position.eye;
|
||||
const vec3 Li = normalize(Liu);
|
||||
const float Ls = shadowFactor( light, 0.0 );
|
||||
const float La = 1.0 / (PI * pow(length(Liu), 2.0));
|
||||
if ( light.power * La * Ls <= LIGHT_POWER_CUTOFF ) continue;
|
||||
|
||||
const float cosLi = max(0.0, dot(surface.normal.eye, Li));
|
||||
const vec3 Lr = light.color.rgb * light.power * La * Ls;
|
||||
#if LAMBERT
|
||||
const vec3 diffuse = surface.material.albedo.rgb;
|
||||
const vec3 specular = vec3(0);
|
||||
#elif PBR
|
||||
const vec3 Lh = normalize(Li + Lo);
|
||||
const float cosLh = max(0.0, dot(surface.normal.eye, Lh));
|
||||
|
||||
const vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
|
||||
const float D = ndfGGX( cosLh, surface.material.roughness * Rs );
|
||||
const float G = gaSchlickGGX(cosLi, cosLo, surface.material.roughness);
|
||||
const vec3 diffuse = mix( vec3(1.0) - F, vec3(0.0), surface.material.metallic ) * surface.material.albedo.rgb;
|
||||
const vec3 specular = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
|
||||
#endif
|
||||
// lightmapped, compute only specular
|
||||
if ( light.type >= 0 && 0 <= surface.material.indexLightmap ) surface.fragment.rgb += (specular) * Lr * cosLi;
|
||||
// point light, compute only diffuse
|
||||
// else if ( abs(light.type) == 1 ) surface.fragment.rgb += (diffuse) * Lr * cosLi;
|
||||
else surface.fragment.rgb += (diffuse + specular) * Lr * cosLi;
|
||||
surface.fragment.a += light.power * La * Ls;
|
||||
}
|
||||
}
|
||||
64
bin/data/shaders/common/shadows.h
Normal file
64
bin/data/shaders/common/shadows.h
Normal file
@ -0,0 +1,64 @@
|
||||
const vec2 poissonDisk[16] = vec2[](
|
||||
vec2( -0.94201624, -0.39906216 ),
|
||||
vec2( 0.94558609, -0.76890725 ),
|
||||
vec2( -0.094184101, -0.92938870 ),
|
||||
vec2( 0.34495938, 0.29387760 ),
|
||||
vec2( -0.91588581, 0.45771432 ),
|
||||
vec2( -0.81544232, -0.87912464 ),
|
||||
vec2( -0.38277543, 0.27676845 ),
|
||||
vec2( 0.97484398, 0.75648379 ),
|
||||
vec2( 0.44323325, -0.97511554 ),
|
||||
vec2( 0.53742981, -0.47373420 ),
|
||||
vec2( -0.26496911, -0.41893023 ),
|
||||
vec2( 0.79197514, 0.19090188 ),
|
||||
vec2( -0.24188840, 0.99706507 ),
|
||||
vec2( -0.81409955, 0.91437590 ),
|
||||
vec2( 0.19984126, 0.78641367 ),
|
||||
vec2( 0.14383161, -0.14100790 )
|
||||
);
|
||||
|
||||
#ifndef SHADOW_SAMPLES
|
||||
#define SHADOW_SAMPLES ubo.shadowSamples
|
||||
#endif
|
||||
|
||||
float shadowFactor( const Light light, float def ) {
|
||||
if ( !validTextureIndex(light.mapIndex) ) {
|
||||
#if VXGI
|
||||
return voxelShadowFactor( light, def );
|
||||
#else
|
||||
return 1.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
vec4 positionClip = light.projection * light.view * vec4(surface.position.world, 1.0);
|
||||
positionClip.xyz /= positionClip.w;
|
||||
|
||||
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
|
||||
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
|
||||
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
|
||||
|
||||
float factor = 1.0;
|
||||
|
||||
// spot light
|
||||
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
|
||||
const float dist = length( positionClip.xy );
|
||||
if ( dist > 0.5 ) return def; //0.0;
|
||||
|
||||
// spot light with attenuation
|
||||
if ( abs(light.type) == 3 ) {
|
||||
factor = 1.0 - (pow(dist * 2,2.0));
|
||||
}
|
||||
}
|
||||
|
||||
const vec2 uv = positionClip.xy * 0.5 + 0.5;
|
||||
const float bias = light.depthBias;
|
||||
const float eyeDepth = positionClip.z;
|
||||
const int samples = int(SHADOW_SAMPLES);
|
||||
if ( samples < 1 ) return eyeDepth < texture(samplerTextures[nonuniformEXT(light.mapIndex)], uv).r - bias ? 0.0 : factor;
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
const int index = int( float(samples) * random(floor(surface.position.world.xyz * 1000.0), i)) % samples;
|
||||
const float lightDepth = texture(samplerTextures[nonuniformEXT(light.mapIndex)], uv + poissonDisk[index] / 700.0 ).r;
|
||||
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
138
bin/data/shaders/common/structs.h
Normal file
138
bin/data/shaders/common/structs.h
Normal file
@ -0,0 +1,138 @@
|
||||
struct Matrices {
|
||||
mat4 view[2];
|
||||
mat4 projection[2];
|
||||
mat4 iView[2];
|
||||
mat4 iProjection[2];
|
||||
mat4 iProjectionView[2];
|
||||
vec4 eyePos[2];
|
||||
mat4 vxgi;
|
||||
};
|
||||
|
||||
struct Cursor {
|
||||
vec2 position;
|
||||
vec2 radius;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
struct Ray {
|
||||
vec3 origin;
|
||||
vec3 direction;
|
||||
|
||||
vec3 position;
|
||||
float distance;
|
||||
};
|
||||
|
||||
struct Space {
|
||||
vec3 eye;
|
||||
vec3 world;
|
||||
};
|
||||
|
||||
struct Fog {
|
||||
vec3 color;
|
||||
float stepScale;
|
||||
|
||||
vec3 offset;
|
||||
float densityScale;
|
||||
|
||||
float densityThreshold;
|
||||
float densityMultiplier;
|
||||
float absorbtion;
|
||||
float padding1;
|
||||
|
||||
vec2 range;
|
||||
float padding2;
|
||||
float padding3;
|
||||
};
|
||||
|
||||
struct Mode {
|
||||
uint type;
|
||||
uint scalar;
|
||||
vec2 padding;
|
||||
vec4 parameters;
|
||||
};
|
||||
|
||||
struct Light {
|
||||
vec3 position;
|
||||
float radius;
|
||||
|
||||
vec3 color;
|
||||
float power;
|
||||
|
||||
int type;
|
||||
int mapIndex;
|
||||
float depthBias;
|
||||
float padding;
|
||||
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
};
|
||||
|
||||
struct Material {
|
||||
vec4 colorBase;
|
||||
vec4 colorEmissive;
|
||||
|
||||
float factorMetallic;
|
||||
float factorRoughness;
|
||||
float factorOcclusion;
|
||||
float factorAlphaCutoff;
|
||||
|
||||
int indexAlbedo;
|
||||
int indexNormal;
|
||||
int indexEmissive;
|
||||
int indexOcclusion;
|
||||
|
||||
int indexMetallicRoughness;
|
||||
int indexAtlas;
|
||||
int indexLightmap;
|
||||
int modeAlpha;
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
int index;
|
||||
int samp;
|
||||
int remap;
|
||||
float blend;
|
||||
|
||||
vec4 lerp;
|
||||
};
|
||||
|
||||
struct DrawCall {
|
||||
int materialIndex;
|
||||
uint materials;
|
||||
int textureIndex;
|
||||
uint textures;
|
||||
};
|
||||
|
||||
struct SurfaceMaterial {
|
||||
uint id;
|
||||
|
||||
vec4 albedo;
|
||||
vec4 indirect;
|
||||
|
||||
float metallic;
|
||||
float roughness;
|
||||
float occlusion;
|
||||
int indexLightmap;
|
||||
|
||||
};
|
||||
|
||||
struct Surface {
|
||||
uint pass;
|
||||
vec2 uv;
|
||||
Space position;
|
||||
Space normal;
|
||||
|
||||
Ray ray;
|
||||
|
||||
SurfaceMaterial material;
|
||||
|
||||
vec4 fragment;
|
||||
} surface;
|
||||
|
||||
struct Voxel {
|
||||
uvec2 id;
|
||||
vec3 position;
|
||||
vec3 normal;
|
||||
vec2 uv;
|
||||
vec4 color;
|
||||
};
|
||||
194
bin/data/shaders/common/vxgi.h
Normal file
194
bin/data/shaders/common/vxgi.h
Normal file
@ -0,0 +1,194 @@
|
||||
// GI
|
||||
struct VoxelInfo {
|
||||
vec3 min;
|
||||
vec3 max;
|
||||
|
||||
float mipmapLevels;
|
||||
float radianceSize;
|
||||
float radianceSizeRecip;
|
||||
} voxelInfo;
|
||||
float cascadePower( uint x ) {
|
||||
return pow(1 + x, ubo.cascadePower);
|
||||
// return max( 1, x * ubo.cascadePower );
|
||||
}
|
||||
uint cascadeIndex( vec3 v ) {
|
||||
float x = max3( abs( v ) );
|
||||
for ( uint cascade = 0; cascade < CASCADES; ++cascade )
|
||||
if ( x / cascadePower(cascade) < 1 - voxelInfo.radianceSizeRecip ) return cascade;
|
||||
return CASCADES - 1;
|
||||
}
|
||||
|
||||
vec4 voxelTrace( inout Ray ray, float aperture, float maxDistance ) {
|
||||
ray.origin += ray.direction * voxelInfo.radianceSizeRecip * 2 * SQRT2;
|
||||
#if VXGI_NDC
|
||||
ray.origin = vec3( ubo.matrices.vxgi * vec4( ray.origin, 1.0 ) );
|
||||
ray.direction = vec3( ubo.matrices.vxgi * vec4( ray.direction, 0.0 ) );
|
||||
uint cascade = cascadeIndex(ray.origin);
|
||||
#else
|
||||
uint cascade = cascadeIndex( vec3( ubo.matrices.vxgi * vec4( ray.origin, 1.0 ) ) );
|
||||
#endif
|
||||
const float granularityRecip = 2.0; // 0.25f * (CASCADES - cascade);
|
||||
const float granularity = 1.0f / granularityRecip;
|
||||
const float occlusionFalloff = 128.0f;
|
||||
const vec3 voxelBounds = voxelInfo.max - voxelInfo.min;
|
||||
const vec3 voxelBoundsRecip = 1.0f / voxelBounds;
|
||||
const float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
const uint maxSteps = uint(voxelInfo.radianceSize * cascadePower(CASCADES-1) * granularityRecip);
|
||||
// box
|
||||
const vec2 rayBoxInfoA = rayBoxDst( voxelInfo.min * cascadePower(cascade), voxelInfo.max * cascadePower(cascade), ray );
|
||||
const vec2 rayBoxInfoB = rayBoxDst( voxelInfo.min * cascadePower(CASCADES-1), voxelInfo.max * cascadePower(CASCADES-1), ray );
|
||||
|
||||
const float tStart = rayBoxInfoA.x;
|
||||
const float tEnd = maxDistance > 0 ? min(maxDistance, rayBoxInfoB.y) : rayBoxInfoB.y;
|
||||
const float tDelta = voxelInfo.radianceSizeRecip * granularityRecip;
|
||||
// marcher
|
||||
ray.distance = tStart;
|
||||
ray.position = vec3(0);
|
||||
|
||||
vec4 radiance = vec4(0);
|
||||
vec3 uvw = vec3(0);
|
||||
float coneDiameter = coneCoefficient * ray.distance;
|
||||
float level = aperture > 0 ? log2( coneDiameter ) : 0;
|
||||
vec4 color = vec4(0);
|
||||
float occlusion = 0.0;
|
||||
uint stepCounter = 0;
|
||||
while ( color.a < 1.0 && occlusion < 1.0 && ray.distance < tEnd && stepCounter++ < maxSteps ) {
|
||||
ray.distance += tDelta * cascadePower(cascade) * max(1, coneDiameter);
|
||||
ray.position = ray.origin + ray.direction * ray.distance;
|
||||
|
||||
#if VXGI_NDC
|
||||
uvw = ray.position;
|
||||
#else
|
||||
uvw = vec3( ubo.matrices.vxgi * vec4( ray.position, 1.0 ) );
|
||||
#endif
|
||||
cascade = cascadeIndex( uvw );
|
||||
uvw = (uvw / cascadePower(cascade)) * 0.5 + 0.5;
|
||||
if ( cascade >= CASCADES || uvw.x < 0.0 || uvw.y < 0.0 || uvw.z < 0.0 || uvw.x >= 1.0 || uvw.y >= 1.0 || uvw.z >= 1.0 ) break;
|
||||
coneDiameter = coneCoefficient * ray.distance;
|
||||
level = aperture > 0 ? log2( coneDiameter ) : 0;
|
||||
if ( level >= voxelInfo.mipmapLevels ) break;
|
||||
radiance = textureLod(voxelRadiance[nonuniformEXT(cascade)], uvw.xzy, level);
|
||||
color += (1.0 - color.a) * radiance;
|
||||
occlusion += ((1.0f - occlusion) * radiance.a) / (1.0f + occlusionFalloff * coneDiameter);
|
||||
}
|
||||
return maxDistance > 0 ? color : vec4(color.rgb, occlusion);
|
||||
// return vec4(color.rgb, occlusion);
|
||||
}
|
||||
vec4 voxelConeTrace( inout Ray ray, float aperture ) {
|
||||
return voxelTrace( ray, aperture, 0 );
|
||||
}
|
||||
vec4 voxelTrace( inout Ray ray, float maxDistance ) {
|
||||
return voxelTrace( ray, 0, maxDistance );
|
||||
}
|
||||
uint voxelShadowsCount = 0;
|
||||
float voxelShadowFactor( const Light light, float def ) {
|
||||
if ( voxelShadowsCount++ > ubo.shadowSamples ) return 1.0;
|
||||
|
||||
const float SHADOW_APERTURE = 0.2;
|
||||
const float DEPTH_BIAS = 0.0;
|
||||
|
||||
Ray ray;
|
||||
ray.direction = normalize( light.position - surface.position.world );
|
||||
ray.origin = surface.position.world + ray.direction * 0.5;
|
||||
float z = distance( surface.position.world, light.position ) - DEPTH_BIAS;
|
||||
return 1.0 - voxelTrace( ray, SHADOW_APERTURE, z ).a;
|
||||
}
|
||||
void indirectLighting() {
|
||||
voxelInfo.radianceSize = textureSize( voxelRadiance[0], 0 ).x;
|
||||
voxelInfo.radianceSizeRecip = 1.0 / voxelInfo.radianceSize;
|
||||
voxelInfo.mipmapLevels = log2(voxelInfo.radianceSize) + 1;
|
||||
#if VXGI_NDC
|
||||
voxelInfo.min = vec3( -1 );
|
||||
voxelInfo.max = vec3( 1 );
|
||||
#else
|
||||
const mat4 inverseOrtho = inverse( ubo.matrices.vxgi );
|
||||
voxelInfo.min = vec3( inverseOrtho * vec4( -1, -1, -1, 1 ) );
|
||||
voxelInfo.max = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) );
|
||||
#endif
|
||||
|
||||
const vec3 P = surface.position.world;
|
||||
const vec3 N = surface.normal.world;
|
||||
|
||||
#if 1
|
||||
const vec3 right = normalize(orthogonal(N));
|
||||
const vec3 up = normalize(cross(right, N));
|
||||
|
||||
const uint CONES_COUNT = 6;
|
||||
const vec3 CONES[] = {
|
||||
N,
|
||||
normalize(N + 0.0f * right + 0.866025f * up),
|
||||
normalize(N + 0.823639f * right + 0.267617f * up),
|
||||
normalize(N + 0.509037f * right + -0.7006629f * up),
|
||||
normalize(N + -0.50937f * right + -0.7006629f * up),
|
||||
normalize(N + -0.823639f * right + 0.267617f * up),
|
||||
};
|
||||
#else
|
||||
const vec3 ortho = normalize(orthogonal(N));
|
||||
const vec3 ortho2 = normalize(cross(ortho, N));
|
||||
|
||||
const vec3 corner = 0.5f * (ortho + ortho2);
|
||||
const vec3 corner2 = 0.5f * (ortho - ortho2);
|
||||
|
||||
const uint CONES_COUNT = 9;
|
||||
const vec3 CONES[] = {
|
||||
N,
|
||||
normalize(mix(N, ortho, 0.5)),
|
||||
normalize(mix(N, -ortho, 0.5)),
|
||||
normalize(mix(N, ortho2, 0.5)),
|
||||
normalize(mix(N, -ortho2, 0.5)),
|
||||
normalize(mix(N, corner, 0.5)),
|
||||
normalize(mix(N, -corner, 0.5)),
|
||||
normalize(mix(N, corner2, 0.5)),
|
||||
normalize(mix(N, -corner2, 0.5)),
|
||||
};
|
||||
#endif
|
||||
|
||||
const float DIFFUSE_CONE_APERTURE = 2.0 * 0.57735f;
|
||||
const float DIFFUSE_INDIRECT_FACTOR = 1.0f / float(CONES_COUNT);
|
||||
|
||||
const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * surface.material.roughness), 0.0174533f, PI); // tan( R * PI * 0.5f * 0.1f );
|
||||
const float SPECULAR_INDIRECT_FACTOR = (1.0 - surface.material.metallic) * 0.5; // 1.0f;
|
||||
|
||||
vec4 indirectDiffuse = vec4(0);
|
||||
vec4 indirectSpecular = vec4(0);
|
||||
|
||||
// outFragColor.rgb = voxelConeTrace( surface.ray, 0 ).rgb; return;
|
||||
if ( DIFFUSE_INDIRECT_FACTOR > 0.0f ) {
|
||||
float weight = PI * 0.25f;
|
||||
for ( uint i = 0; i < CONES_COUNT; ++i ) {
|
||||
Ray ray;
|
||||
ray.direction = CONES[i].xyz;
|
||||
ray.origin = P; // + ray.direction;
|
||||
indirectDiffuse += voxelConeTrace( ray, DIFFUSE_CONE_APERTURE ) * weight;
|
||||
weight = PI * 0.15f;
|
||||
}
|
||||
surface.material.occlusion = 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0);
|
||||
// outFragColor.rgb = indirectDiffuse.rgb; return;
|
||||
// outFragColor.rgb = vec3(surface.material.occlusion); return;
|
||||
}
|
||||
if ( SPECULAR_INDIRECT_FACTOR > 0.0f ) {
|
||||
const vec3 R = reflect( normalize(P - surface.ray.origin), N );
|
||||
Ray ray;
|
||||
ray.direction = R;
|
||||
ray.origin = P; // + ray.direction;
|
||||
indirectSpecular = voxelConeTrace( ray, SPECULAR_CONE_APERTURE );
|
||||
// outFragColor.rgb = indirectSpecular.rgb; return;
|
||||
if ( length(indirectSpecular) < 0.0125 ) {
|
||||
// indirectSpecular += (1.0 - indirectSpecular.a) * texture( samplerSkybox, R );
|
||||
}
|
||||
}
|
||||
|
||||
surface.material.indirect = indirectDiffuse * DIFFUSE_INDIRECT_FACTOR + indirectSpecular * SPECULAR_INDIRECT_FACTOR;
|
||||
// outFragColor.rgb = surface.material.indirect.rgb; return;
|
||||
#if DEFERRED_SAMPLING
|
||||
// deferred sampling doesn't have a blended albedo buffer
|
||||
// in place we'll just cone trace behind the window
|
||||
if ( surface.material.albedo.a < 1.0 ) {
|
||||
Ray ray;
|
||||
ray.direction = surface.ray.direction;
|
||||
ray.origin = surface.position.world; // + ray.direction;
|
||||
vec4 radiance = voxelConeTrace( ray, surface.material.albedo.a );
|
||||
surface.fragment.rgb += (1.0 - surface.material.albedo.a) * radiance.rgb;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (location = 0) in vec2 inPos;
|
||||
layout (location = 1) in vec2 inUv;
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
|
||||
layout (binding = 1) uniform sampler samp;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (location = 0) in vec2 inPos;
|
||||
layout (location = 1) in vec2 inUv;
|
||||
|
||||
67
bin/data/shaders/display/mip.gaussian.comp.glsl
Normal file
67
bin/data/shaders/display/mip.gaussian.comp.glsl
Normal file
@ -0,0 +1,67 @@
|
||||
#version 450
|
||||
#pragma shader_stage(compute)
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
|
||||
layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
|
||||
|
||||
layout (constant_id = 0) const uint CASCADES = 16;
|
||||
layout (constant_id = 1) const uint MIPS = 16;
|
||||
|
||||
layout( push_constant ) uniform PushBlock {
|
||||
uint cascade;
|
||||
uint mip;
|
||||
} PushConstant;
|
||||
|
||||
layout (binding = 1, rg16f) uniform volatile coherent image3D voxelRadiance[CASCADES * MIPS];
|
||||
|
||||
const float gaussianWeights[] = {
|
||||
//Top slice
|
||||
1 / 64.0f,
|
||||
1 / 32.0f,
|
||||
1 / 64.0f,
|
||||
1 / 32.0f,
|
||||
1 / 16.0f,
|
||||
1 / 32.0f,
|
||||
1 / 64.0f,
|
||||
1 / 32.0f,
|
||||
1 / 64.0f,
|
||||
|
||||
//Center slice
|
||||
1 / 32.0f,
|
||||
1 / 16.0f,
|
||||
1 / 32.0f,
|
||||
1 / 16.0f,
|
||||
1 / 4.0f,
|
||||
1 / 16.0f,
|
||||
1 / 32.0f,
|
||||
1 / 16.0f,
|
||||
1 / 32.0f,
|
||||
|
||||
//Bottom slice
|
||||
1 / 64.0f,
|
||||
1 / 32.0f,
|
||||
1 / 64.0f,
|
||||
1 / 32.0f,
|
||||
1 / 16.0f,
|
||||
1 / 32.0f,
|
||||
1 / 64.0f,
|
||||
1 / 32.0f,
|
||||
1 / 64.0f,
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec3 inUVW = ivec3(gl_GlobalInvocationID.xyz) * 2;
|
||||
const ivec3 outUVW = ivec3(gl_GlobalInvocationID.xyz);
|
||||
const uint CASCADE_IN = PushConstant.cascade * CASCADES + PushConstant.mip;
|
||||
const uint CASCADE_OUT = PushConstant.cascade * CASCADES + (PushConstant.mip + 1);
|
||||
|
||||
vec4 color = vec4(0);
|
||||
for ( int z = -1; z <= 1; ++z ) {
|
||||
for ( int y = -1; y <= 1; ++y ) {
|
||||
for ( int x = -1; x <= 1; ++x ) {
|
||||
color += imageLoad( voxelRadiance[CASCADE_IN], inUVW + ivec3(x,y,z) ) * gaussianWeights[x + 1 + (y + 1) * 3 + (z + 1) * 9];
|
||||
}
|
||||
}
|
||||
}
|
||||
imageStore(voxelRadiance[CASCADE_OUT], ivec3(outUVW), vec4(color));
|
||||
}
|
||||
@ -1,9 +1,13 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
|
||||
#define MULTISAMPLING 1
|
||||
#define TEXTURES 1
|
||||
#define DEFERRED_SAMPLING 0
|
||||
#define CAN_DISCARD 1
|
||||
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
#if !MULTISAMPLING
|
||||
layout (binding = 1) uniform usampler2D textureId;
|
||||
@ -23,22 +27,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct Cursor {
|
||||
vec2 position;
|
||||
vec2 radius;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
vec4 resolve( sampler2DMS t, ivec2 uv ) {
|
||||
vec4 resolved = vec4(0);
|
||||
int samples = textureSamples(t);
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
resolved += texelFetch(t, uv, i);
|
||||
}
|
||||
resolved /= float(samples);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in float inAlpha;
|
||||
layout (location = 2) in Cursor inCursor;
|
||||
@ -46,27 +34,13 @@ layout (location = 2) in Cursor inCursor;
|
||||
layout (location = 0) out vec4 outAlbedo;
|
||||
|
||||
void main() {
|
||||
#if !MULTISAMPLING
|
||||
ivec2 screenSize = textureSize(textureId, 0);
|
||||
#else
|
||||
ivec2 screenSize = textureSize(textureId);
|
||||
#endif
|
||||
ivec2 uv = ivec2(inUv * screenSize);
|
||||
if ( inCursor.radius.x <= 0 || inCursor.radius.y <= 0 ) {
|
||||
#if !MULTISAMPLING
|
||||
outAlbedo = texture( textureAlbedo, uv ).rgba;
|
||||
#else
|
||||
outAlbedo = resolve( textureAlbedo, uv );
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
float dist = pow(inUv.x - inCursor.position.x, 2) / pow(inCursor.radius.x, 2) + pow(inUv.y - inCursor.position.y, 2) / pow(inCursor.radius.y, 2);
|
||||
|
||||
#if !MULTISAMPLING
|
||||
outAlbedo = texture( textureAlbedo, inUv );
|
||||
#else
|
||||
outAlbedo = resolve( textureAlbedo, uv );
|
||||
outAlbedo = resolve( textureAlbedo, ivec2(inUv * textureSize(textureId)) );
|
||||
#endif
|
||||
if ( inCursor.radius.x <= 0 || inCursor.radius.y <= 0 ) return;
|
||||
float dist = pow(inUv.x - inCursor.position.x, 2) / pow(inCursor.radius.x, 2) + pow(inUv.y - inCursor.position.y, 2) / pow(inCursor.radius.y, 2);
|
||||
|
||||
if ( dist <= 1 ) {
|
||||
float attenuation = dist;
|
||||
|
||||
51
bin/data/shaders/display/rendertarget.no-msaa.frag.glsl
Normal file
51
bin/data/shaders/display/rendertarget.no-msaa.frag.glsl
Normal file
@ -0,0 +1,51 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
|
||||
#define TEXTURES 1
|
||||
#define DEFERRED_SAMPLING 0
|
||||
#define MULTISAMPLING 0
|
||||
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
#if !MULTISAMPLING
|
||||
layout (binding = 1) uniform usampler2D textureId;
|
||||
layout (binding = 2) uniform sampler2D textureNormals;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (binding = 3) uniform sampler2D textureUvs;
|
||||
#else
|
||||
layout (binding = 3) uniform sampler2D textureAlbedo;
|
||||
#endif
|
||||
#else
|
||||
layout (binding = 1) uniform usampler2DMS textureId;
|
||||
layout (binding = 2) uniform sampler2DMS textureNormals;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (binding = 3) uniform sampler2DMS textureUvs;
|
||||
#else
|
||||
layout (binding = 3) uniform sampler2DMS textureAlbedo;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in float inAlpha;
|
||||
layout (location = 2) in Cursor inCursor;
|
||||
|
||||
layout (location = 0) out vec4 outAlbedo;
|
||||
|
||||
void main() {
|
||||
#if !MULTISAMPLING
|
||||
outAlbedo = texture( textureAlbedo, inUv );
|
||||
#else
|
||||
outAlbedo = resolve( textureAlbedo, ivec2(inUv * textureSize(textureId)) );
|
||||
#endif
|
||||
if ( inCursor.radius.x <= 0 || inCursor.radius.y <= 0 ) return;
|
||||
float dist = pow(inUv.x - inCursor.position.x, 2) / pow(inCursor.radius.x, 2) + pow(inUv.y - inCursor.position.y, 2) / pow(inCursor.radius.y, 2);
|
||||
|
||||
if ( dist <= 1 ) {
|
||||
float attenuation = dist;
|
||||
outAlbedo.rgb = mix( inCursor.color.rgb * inCursor.color.a, outAlbedo.rgb, attenuation );
|
||||
}
|
||||
if ( inAlpha < 1.0 ) outAlbedo.a = inAlpha;
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (location = 0) in vec2 inPos;
|
||||
layout (location = 1) in vec2 inUv;
|
||||
|
||||
@ -1,602 +1,14 @@
|
||||
#version 450
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define MULTISAMPLING 1
|
||||
#define DEFERRED_SAMPLING 1
|
||||
|
||||
#define FOG 1
|
||||
#define FOG_RAY_MARCH 1
|
||||
|
||||
#define WHITENOISE 1
|
||||
#define GAMMA_CORRECT 1
|
||||
#define TONE_MAP 1
|
||||
|
||||
#define LAMBERT 0
|
||||
#define PBR 1
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
const float EPSILON = 0.00001;
|
||||
const float SQRT2 = 1.41421356237;
|
||||
|
||||
const float LIGHT_POWER_CUTOFF = 0.005;
|
||||
|
||||
const vec2 poissonDisk[16] = vec2[](
|
||||
vec2( -0.94201624, -0.39906216 ),
|
||||
vec2( 0.94558609, -0.76890725 ),
|
||||
vec2( -0.094184101, -0.92938870 ),
|
||||
vec2( 0.34495938, 0.29387760 ),
|
||||
vec2( -0.91588581, 0.45771432 ),
|
||||
vec2( -0.81544232, -0.87912464 ),
|
||||
vec2( -0.38277543, 0.27676845 ),
|
||||
vec2( 0.97484398, 0.75648379 ),
|
||||
vec2( 0.44323325, -0.97511554 ),
|
||||
vec2( 0.53742981, -0.47373420 ),
|
||||
vec2( -0.26496911, -0.41893023 ),
|
||||
vec2( 0.79197514, 0.19090188 ),
|
||||
vec2( -0.24188840, 0.99706507 ),
|
||||
vec2( -0.81409955, 0.91437590 ),
|
||||
vec2( 0.19984126, 0.78641367 ),
|
||||
vec2( 0.14383161, -0.14100790 )
|
||||
);
|
||||
|
||||
layout (constant_id = 0) const uint TEXTURES = 512;
|
||||
|
||||
struct Matrices {
|
||||
mat4 view[2];
|
||||
mat4 projection[2];
|
||||
mat4 iView[2];
|
||||
mat4 iProjection[2];
|
||||
mat4 iProjectionView[2];
|
||||
vec4 eyePos[2];
|
||||
mat4 vxgi;
|
||||
};
|
||||
|
||||
struct Ray {
|
||||
vec3 origin;
|
||||
vec3 direction;
|
||||
|
||||
vec3 position;
|
||||
float distance;
|
||||
};
|
||||
|
||||
struct Space {
|
||||
vec3 eye;
|
||||
vec3 world;
|
||||
};
|
||||
|
||||
struct Fog {
|
||||
vec3 color;
|
||||
float stepScale;
|
||||
|
||||
vec3 offset;
|
||||
float densityScale;
|
||||
|
||||
float densityThreshold;
|
||||
float densityMultiplier;
|
||||
float absorbtion;
|
||||
float padding1;
|
||||
|
||||
vec2 range;
|
||||
float padding2;
|
||||
float padding3;
|
||||
};
|
||||
|
||||
struct Mode {
|
||||
uint type;
|
||||
uint scalar;
|
||||
vec2 padding;
|
||||
vec4 parameters;
|
||||
};
|
||||
|
||||
struct Light {
|
||||
vec3 position;
|
||||
float radius;
|
||||
|
||||
vec3 color;
|
||||
float power;
|
||||
|
||||
int type;
|
||||
int mapIndex;
|
||||
float depthBias;
|
||||
float padding;
|
||||
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
};
|
||||
|
||||
struct Material {
|
||||
vec4 colorBase;
|
||||
vec4 colorEmissive;
|
||||
|
||||
float factorMetallic;
|
||||
float factorRoughness;
|
||||
float factorOcclusion;
|
||||
float factorAlphaCutoff;
|
||||
|
||||
int indexAlbedo;
|
||||
int indexNormal;
|
||||
int indexEmissive;
|
||||
int indexOcclusion;
|
||||
|
||||
int indexMetallicRoughness;
|
||||
int indexAtlas;
|
||||
int indexLightmap;
|
||||
int modeAlpha;
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
int index;
|
||||
int samp;
|
||||
int remap;
|
||||
float blend;
|
||||
|
||||
vec4 lerp;
|
||||
};
|
||||
|
||||
struct DrawCall {
|
||||
int materialIndex;
|
||||
uint materials;
|
||||
int textureIndex;
|
||||
uint textures;
|
||||
};
|
||||
|
||||
struct SurfaceMaterial {
|
||||
uint id;
|
||||
|
||||
vec4 albedo;
|
||||
vec4 indirect;
|
||||
|
||||
float metallic;
|
||||
float roughness;
|
||||
float occlusion;
|
||||
};
|
||||
|
||||
struct Surface {
|
||||
vec2 uv;
|
||||
Space position;
|
||||
Space normal;
|
||||
|
||||
Ray ray;
|
||||
|
||||
SurfaceMaterial material;
|
||||
|
||||
vec4 fragment;
|
||||
} surface;
|
||||
|
||||
struct Voxel {
|
||||
uvec2 id;
|
||||
vec3 position;
|
||||
vec3 normal;
|
||||
vec2 uv;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
#if !MULTISAMPLING
|
||||
layout (input_attachment_index = 0, binding = 0) uniform usubpassInput samplerId;
|
||||
layout (input_attachment_index = 1, binding = 1) uniform subpassInput samplerNormal;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerUv;
|
||||
#else
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerAlbedo;
|
||||
#endif
|
||||
layout (input_attachment_index = 3, binding = 3) uniform subpassInput samplerDepth;
|
||||
#else
|
||||
layout (input_attachment_index = 0, binding = 0) uniform usubpassInputMS samplerId;
|
||||
layout (input_attachment_index = 1, binding = 1) uniform subpassInputMS samplerNormal;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerUv;
|
||||
#else
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerAlbedo;
|
||||
#endif
|
||||
layout (input_attachment_index = 3, binding = 3) uniform subpassInputMS samplerDepth;
|
||||
#endif
|
||||
|
||||
layout (binding = 4) uniform UBO {
|
||||
Matrices matrices;
|
||||
|
||||
Mode mode;
|
||||
Fog fog;
|
||||
|
||||
uint lights;
|
||||
uint materials;
|
||||
uint textures;
|
||||
uint drawCalls;
|
||||
|
||||
vec3 ambient;
|
||||
float gamma;
|
||||
|
||||
float exposure;
|
||||
uint msaa;
|
||||
uint shadowSamples;
|
||||
uint padding1;
|
||||
} ubo;
|
||||
|
||||
layout (std140, binding = 5) readonly buffer Lights {
|
||||
Light lights[];
|
||||
};
|
||||
layout (std140, binding = 6) readonly buffer Materials {
|
||||
Material materials[];
|
||||
};
|
||||
layout (std140, binding = 7) readonly buffer Textures {
|
||||
Texture textures[];
|
||||
};
|
||||
layout (std140, binding = 8) readonly buffer DrawCalls {
|
||||
DrawCall drawCalls[];
|
||||
};
|
||||
|
||||
layout (binding = 9) uniform sampler3D samplerNoise;
|
||||
layout (binding = 10) uniform samplerCube samplerSkybox;
|
||||
layout (binding = 11) uniform sampler2D samplerTextures[TEXTURES];
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in flat uint inPushConstantPass;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
// GGX/Towbridge-Reitz normal distribution function.
|
||||
// Uses Disney's reparametrization of alpha = roughness^2.
|
||||
float ndfGGX(float cosLh, float roughness) {
|
||||
const float alpha = roughness * roughness;
|
||||
const float alphaSq = alpha * alpha;
|
||||
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
|
||||
return alphaSq / (PI * denom * denom);
|
||||
}
|
||||
|
||||
// Single term for separable Schlick-GGX below.
|
||||
float gaSchlickG1(float cosTheta, float k) {
|
||||
return cosTheta / (cosTheta * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
|
||||
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
|
||||
const float r = roughness + 1.0;
|
||||
const float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
|
||||
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
|
||||
}
|
||||
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
float random(vec3 seed, int i){
|
||||
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
|
||||
}
|
||||
|
||||
// Returns a vector that is orthogonal to u.
|
||||
vec3 orthogonal(vec3 u){
|
||||
u = normalize(u);
|
||||
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
|
||||
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
|
||||
}
|
||||
float rand2(vec2 co){
|
||||
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453);
|
||||
}
|
||||
float rand3(vec3 co){
|
||||
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
|
||||
}
|
||||
void whitenoise(inout vec3 color) {
|
||||
const float flicker = ubo.mode.parameters.x;
|
||||
const float pieces = ubo.mode.parameters.y;
|
||||
const float blend = ubo.mode.parameters.z;
|
||||
const float time = ubo.mode.parameters.w;
|
||||
if ( blend < 0.0001 ) return;
|
||||
const float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
|
||||
const float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
|
||||
color = mix( color, vec3(whiteNoise), blend );
|
||||
}
|
||||
vec3 gamma( vec3 i ) {
|
||||
return pow(i.rgb, vec3(1.0 / 2.2));
|
||||
}
|
||||
|
||||
vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, in Ray ray ) {
|
||||
const vec3 t0 = (boundsMin - ray.origin) / ray.direction;
|
||||
const vec3 t1 = (boundsMax - ray.origin) / ray.direction;
|
||||
const vec3 tmin = min(t0, t1);
|
||||
const vec3 tmax = max(t0, t1);
|
||||
const float tStart = max(0, max( max(tmin.x, tmin.y), tmin.z ));
|
||||
const float tEnd = max(0, min( tmax.x, min(tmax.y, tmax.z) ) - tStart);
|
||||
return vec2(tStart, tEnd);
|
||||
}
|
||||
|
||||
float sampleDensity( vec3 position ) {
|
||||
const vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
|
||||
return max(0, texture(samplerNoise, uvw).r - ubo.fog.densityThreshold) * ubo.fog.densityMultiplier;
|
||||
}
|
||||
|
||||
void fog( in Ray ray, inout vec3 i, float scale ) {
|
||||
if ( ubo.fog.stepScale <= 0 ) return;
|
||||
if ( ubo.fog.range.x == 0 || ubo.fog.range.y == 0 ) return;
|
||||
|
||||
#if FOG_RAY_MARCH
|
||||
const float range = ubo.fog.range.y;
|
||||
const vec3 boundsMin = vec3(-range,-range,-range) + ray.origin;
|
||||
const vec3 boundsMax = vec3(range,range,range) + ray.origin;
|
||||
const int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
|
||||
|
||||
const vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, ray );
|
||||
const float dstToBox = rayBoxInfo.x;
|
||||
const float dstInsideBox = rayBoxInfo.y;
|
||||
const float depth = surface.position.eye.z;
|
||||
|
||||
float lightEnergy = 0;
|
||||
// march
|
||||
if ( 0 <= dstInsideBox && dstToBox <= depth ) {
|
||||
float dstTravelled = 0;
|
||||
float stepSize = dstInsideBox / numSteps;
|
||||
float dstLimit = min( depth - dstToBox, dstInsideBox );
|
||||
float totalDensity = 0;
|
||||
float transmittance = 1;
|
||||
while ( dstTravelled < dstLimit ) {
|
||||
vec3 rayPos = ray.origin + ray.direction * (dstToBox + dstTravelled);
|
||||
float density = sampleDensity(rayPos);
|
||||
if ( density > 0 ) {
|
||||
transmittance *= exp(-density * stepSize * ubo.fog.absorbtion);
|
||||
if ( transmittance < 0.01 ) break;
|
||||
}
|
||||
dstTravelled += stepSize;
|
||||
}
|
||||
i.rgb = mix(ubo.fog.color.rgb, i.rgb, transmittance);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
const vec3 color = ubo.fog.color.rgb;
|
||||
const float inner = ubo.fog.range.x;
|
||||
const float outer = ubo.fog.range.y * scale;
|
||||
const float distance = length(-surface.position.eye);
|
||||
const float factor = clamp( (distance - inner) / (outer - inner), 0.0, 1.0 );
|
||||
|
||||
i.rgb = mix(i.rgb, color, factor);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 decodeNormals( vec2 enc ) {
|
||||
const vec2 ang = enc*2-1;
|
||||
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
|
||||
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
|
||||
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
|
||||
}
|
||||
float wrap( float i ) {
|
||||
return fract(i);
|
||||
}
|
||||
vec2 wrap( vec2 uv ) {
|
||||
return vec2( wrap( uv.x ), wrap( uv.y ) );
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
const vec2 dx_vtc = dFdx(uv);
|
||||
const vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
bool validTextureIndex( int textureIndex ) {
|
||||
return 0 <= textureIndex; // && textureIndex < ubo.textures;
|
||||
}
|
||||
vec4 resolve( subpassInputMS t ) {
|
||||
const int samples = int(ubo.msaa);
|
||||
vec4 resolved = vec4(0);
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
resolved += subpassLoad(t, i);
|
||||
}
|
||||
resolved /= vec4(samples);
|
||||
return resolved;
|
||||
}
|
||||
uvec4 resolve( usubpassInputMS t ) {
|
||||
const int samples = int(ubo.msaa);
|
||||
uvec4 resolved = uvec4(0);
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
resolved += subpassLoad(t, i);
|
||||
}
|
||||
resolved /= uvec4(samples);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
float shadowFactor( const Light light, float def ) {
|
||||
if ( !validTextureIndex(light.mapIndex) ) return 1.0;
|
||||
|
||||
vec4 positionClip = light.projection * light.view * vec4(surface.position.world, 1.0);
|
||||
positionClip.xyz /= positionClip.w;
|
||||
|
||||
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
|
||||
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
|
||||
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
|
||||
|
||||
float factor = 1.0;
|
||||
|
||||
// spot light
|
||||
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
|
||||
const float dist = length( positionClip.xy );
|
||||
if ( dist > 0.5 ) return def; //0.0;
|
||||
|
||||
// spot light with attenuation
|
||||
if ( abs(light.type) == 3 ) {
|
||||
factor = 1.0 - (pow(dist * 2,2.0));
|
||||
}
|
||||
}
|
||||
|
||||
const vec2 uv = positionClip.xy * 0.5 + 0.5;
|
||||
const float bias = light.depthBias;
|
||||
const float eyeDepth = positionClip.z;
|
||||
const int samples = int(ubo.shadowSamples);
|
||||
if ( samples < 1 ) return eyeDepth < texture(samplerTextures[light.mapIndex], uv).r - bias ? 0.0 : factor;
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
const int index = int( float(samples) * random(floor(surface.position.world.xyz * 1000.0), i)) % samples;
|
||||
const float lightDepth = texture(samplerTextures[light.mapIndex], uv + poissonDisk[index] / 700.0 ).r;
|
||||
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
|
||||
vec4 postProcess() {
|
||||
#if FOG
|
||||
fog( surface.ray, surface.fragment.rgb, surface.fragment.a );
|
||||
#endif
|
||||
#if TONE_MAP
|
||||
surface.fragment.rgb = vec3(1.0) - exp(-surface.fragment.rgb * ubo.exposure);
|
||||
#endif
|
||||
#if GAMMA_CORRECT
|
||||
surface.fragment.rgb = pow(surface.fragment.rgb, vec3(1.0 / ubo.gamma));
|
||||
#endif
|
||||
#if WHITENOISE
|
||||
if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) whitenoise(surface.fragment.rgb);
|
||||
#endif
|
||||
|
||||
return vec4(surface.fragment.rgb,1);
|
||||
}
|
||||
#include "./subpass.h"
|
||||
|
||||
void main() {
|
||||
{
|
||||
#if !MULTISAMPLING
|
||||
const float depth = subpassLoad(samplerDepth).r;
|
||||
#else
|
||||
const float depth = resolve( samplerDepth ).r;
|
||||
#endif
|
||||
populateSurface();
|
||||
|
||||
vec4 positionEye = ubo.matrices.iProjection[inPushConstantPass] * vec4(inUv * 2.0 - 1.0, depth, 1.0);
|
||||
positionEye /= positionEye.w;
|
||||
surface.position.eye = positionEye.xyz;
|
||||
surface.position.world = vec3( ubo.matrices.iView[inPushConstantPass] * positionEye );
|
||||
}
|
||||
#if 0
|
||||
{
|
||||
const vec4 near4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
const vec3 ambient = ubo.ambient.rgb * surface.material.occlusion;
|
||||
surface.fragment.rgb += (0 <= surface.material.indexLightmap) ? (surface.material.albedo.rgb + ambient) : (surface.material.albedo.rgb * ambient);
|
||||
|
||||
surface.ray.origin = near3;
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
}
|
||||
// separate our ray direction due to floating point precision problems
|
||||
{
|
||||
const mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
|
||||
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
}
|
||||
#else
|
||||
{
|
||||
const mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
|
||||
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
surface.ray.origin = ubo.matrices.eyePos[inPushConstantPass].xyz;
|
||||
}
|
||||
#endif
|
||||
#if !MULTISAMPLING
|
||||
surface.normal.world = decodeNormals( subpassLoad(samplerNormal).xy );
|
||||
const uvec2 ID = subpassLoad(samplerId).xy;
|
||||
#else
|
||||
surface.normal.world = decodeNormals( resolve(samplerNormal).xy );
|
||||
const uvec2 ID = resolve(samplerId).xy;
|
||||
#endif
|
||||
surface.normal.eye = vec3( ubo.matrices.view[inPushConstantPass] * vec4(surface.normal.world, 0.0) );
|
||||
|
||||
if ( ID.x == 0 || ID.y == 0 ) {
|
||||
surface.fragment.rgb = texture( samplerSkybox, surface.ray.direction ).rgb;
|
||||
surface.fragment.a = 0.0;
|
||||
outFragColor = postProcess();
|
||||
return;
|
||||
}
|
||||
const uint drawId = ID.x - 1;
|
||||
const DrawCall drawCall = drawCalls[drawId];
|
||||
surface.material.id = ID.y + drawCall.materialIndex - 1;
|
||||
const Material material = materials[surface.material.id];
|
||||
surface.material.albedo = material.colorBase;
|
||||
surface.fragment = material.colorEmissive;
|
||||
#if DEFERRED_SAMPLING
|
||||
#if !MULTISAMPLING
|
||||
surface.uv = subpassLoad(samplerUv).xy;
|
||||
#else
|
||||
surface.uv = resolve(samplerUv).xy;
|
||||
#endif
|
||||
const float mip = mipLevel(inUv.xy);
|
||||
const bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
|
||||
Texture textureAtlas;
|
||||
if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas];
|
||||
if ( validTextureIndex( drawCall.textureIndex + material.indexAlbedo ) ) {
|
||||
const Texture t = textures[drawCall.textureIndex + material.indexAlbedo];
|
||||
surface.material.albedo = textureLod( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, surface.uv ) : surface.uv, mip );
|
||||
}
|
||||
// OPAQUE
|
||||
if ( material.modeAlpha == 0 ) {
|
||||
surface.material.albedo.a = 1;
|
||||
// BLEND
|
||||
} else if ( material.modeAlpha == 1 ) {
|
||||
|
||||
// MASK
|
||||
} else if ( material.modeAlpha == 2 ) {
|
||||
|
||||
}
|
||||
// Emissive textures
|
||||
if ( validTextureIndex( drawCall.textureIndex + material.indexEmissive ) ) {
|
||||
const Texture t = textures[drawCall.textureIndex + material.indexEmissive];
|
||||
surface.fragment += textureLod( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, surface.uv ) : surface.uv, mip );
|
||||
}
|
||||
#else
|
||||
#if !MULTISAMPLING
|
||||
surface.material.albedo = subpassLoad(samplerAlbedo);
|
||||
#else
|
||||
surface.material.albedo = resolve(samplerAlbedo);
|
||||
#endif
|
||||
#endif
|
||||
surface.material.metallic = material.factorMetallic;
|
||||
surface.material.roughness = material.factorRoughness;
|
||||
surface.material.occlusion = material.factorOcclusion;
|
||||
|
||||
if ( 0 <= material.indexLightmap ) {
|
||||
surface.fragment.rgb += surface.material.albedo.rgb + ubo.ambient.rgb * surface.material.occlusion;
|
||||
} else {
|
||||
surface.fragment.rgb += surface.material.albedo.rgb * ubo.ambient.rgb * surface.material.occlusion;
|
||||
}
|
||||
|
||||
// corrections
|
||||
surface.material.roughness *= 4.0;
|
||||
|
||||
{
|
||||
const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic);
|
||||
const vec3 Lo = normalize( -surface.position.eye );
|
||||
const float cosLo = max(0.0, dot(surface.normal.eye, Lo));
|
||||
|
||||
for ( uint i = 0; i < ubo.lights; ++i ) {
|
||||
const Light light = lights[i];
|
||||
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
|
||||
const vec3 Lp = light.position;
|
||||
const vec3 Liu = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position, 1)) - surface.position.eye;
|
||||
const vec3 Li = normalize(Liu);
|
||||
#if VXGI_SHADOWS
|
||||
const float Ls = i < ubo.shadowSamples ? shadowFactor( light, 0.0 ) : 1.0;
|
||||
#else
|
||||
const float Ls = shadowFactor( light, 0.0 );
|
||||
#endif
|
||||
const float La = 1.0 / (PI * pow(length(Liu), 2.0));
|
||||
if ( light.power * La * Ls <= LIGHT_POWER_CUTOFF ) continue;
|
||||
|
||||
const float cosLi = max(0.0, dot(surface.normal.eye, Li));
|
||||
const vec3 Lr = light.color.rgb * light.power * La * Ls;
|
||||
#if LAMBERT
|
||||
const vec3 diffuse = surface.material.albedo.rgb;
|
||||
const vec3 specular = vec3(0);
|
||||
#elif PBR
|
||||
const vec3 Lh = normalize(Li + Lo);
|
||||
const float cosLh = max(0.0, dot(surface.normal.eye, Lh));
|
||||
|
||||
const vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
|
||||
const float D = ndfGGX( cosLh, surface.material.roughness );
|
||||
const float G = gaSchlickGGX(cosLi, cosLo, surface.material.roughness);
|
||||
const vec3 diffuse = mix( vec3(1.0) - F, vec3(0.0), surface.material.metallic ) * surface.material.albedo.rgb;
|
||||
const vec3 specular = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
|
||||
#endif
|
||||
// lightmapped, compute only specular
|
||||
if ( light.type >= 0 && 0 <= material.indexLightmap ) surface.fragment.rgb += (specular) * Lr * cosLi;
|
||||
// point light, compute only diffuse
|
||||
// else if ( abs(light.type) == 1 ) surface.fragment.rgb += (diffuse) * Lr * cosLi;
|
||||
else surface.fragment.rgb += (diffuse + specular) * Lr * cosLi;
|
||||
surface.fragment.a += light.power * La * Ls;
|
||||
}
|
||||
}
|
||||
|
||||
outFragColor = postProcess();
|
||||
pbr();
|
||||
postProcess();
|
||||
}
|
||||
219
bin/data/shaders/display/subpass.h
Normal file
219
bin/data/shaders/display/subpass.h
Normal file
@ -0,0 +1,219 @@
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
#define MAX_TEXTURES TEXTURES
|
||||
#include "../common/macros.h"
|
||||
|
||||
layout (constant_id = 0) const uint TEXTURES = 512;
|
||||
#if VXGI
|
||||
layout (constant_id = 1) const uint CASCADES = 16;
|
||||
#endif
|
||||
|
||||
#if !MULTISAMPLING
|
||||
layout (input_attachment_index = 0, binding = 0) uniform usubpassInput samplerId;
|
||||
layout (input_attachment_index = 1, binding = 1) uniform subpassInput samplerNormal;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerUv;
|
||||
#else
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerAlbedo;
|
||||
#endif
|
||||
layout (input_attachment_index = 3, binding = 3) uniform subpassInput samplerDepth;
|
||||
#else
|
||||
layout (input_attachment_index = 0, binding = 0) uniform usubpassInputMS samplerId;
|
||||
layout (input_attachment_index = 1, binding = 1) uniform subpassInputMS samplerNormal;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerUv;
|
||||
#else
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerAlbedo;
|
||||
#endif
|
||||
layout (input_attachment_index = 3, binding = 3) uniform subpassInputMS samplerDepth;
|
||||
#endif
|
||||
|
||||
#include "../common/structs.h"
|
||||
|
||||
layout (binding = 4) uniform UBO {
|
||||
Matrices matrices;
|
||||
|
||||
Mode mode;
|
||||
Fog fog;
|
||||
|
||||
uint lights;
|
||||
uint materials;
|
||||
uint textures;
|
||||
uint drawCalls;
|
||||
|
||||
vec3 ambient;
|
||||
float gamma;
|
||||
|
||||
float exposure;
|
||||
uint msaa;
|
||||
uint shadowSamples;
|
||||
float cascadePower;
|
||||
} ubo;
|
||||
|
||||
layout (std140, binding = 5) readonly buffer Lights {
|
||||
Light lights[];
|
||||
};
|
||||
layout (std140, binding = 6) readonly buffer Materials {
|
||||
Material materials[];
|
||||
};
|
||||
layout (std140, binding = 7) readonly buffer Textures {
|
||||
Texture textures[];
|
||||
};
|
||||
layout (std140, binding = 8) readonly buffer DrawCalls {
|
||||
DrawCall drawCalls[];
|
||||
};
|
||||
|
||||
#if VXGI
|
||||
layout (binding = 9) uniform usampler3D voxelId[CASCADES];
|
||||
layout (binding = 10) uniform sampler3D voxelUv[CASCADES];
|
||||
layout (binding = 11) uniform sampler3D voxelNormal[CASCADES];
|
||||
layout (binding = 12) uniform sampler3D voxelRadiance[CASCADES];
|
||||
|
||||
layout (binding = 13) uniform sampler3D samplerNoise;
|
||||
layout (binding = 14) uniform samplerCube samplerSkybox;
|
||||
layout (binding = 15) uniform sampler2D samplerTextures[TEXTURES];
|
||||
#else
|
||||
layout (binding = 9) uniform sampler3D samplerNoise;
|
||||
layout (binding = 10) uniform samplerCube samplerSkybox;
|
||||
layout (binding = 11) uniform sampler2D samplerTextures[TEXTURES];
|
||||
#endif
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in flat uint inPushConstantPass;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
#include "../common/functions.h"
|
||||
#include "../common/fog.h"
|
||||
#include "../common/pbr.h"
|
||||
#if VXGI
|
||||
#include "../common/vxgi.h"
|
||||
#endif
|
||||
#include "../common/shadows.h"
|
||||
|
||||
void postProcess() {
|
||||
#if FOG
|
||||
fog( surface.ray, surface.fragment.rgb, surface.fragment.a );
|
||||
#endif
|
||||
#if TONE_MAP
|
||||
surface.fragment.rgb = vec3(1.0) - exp(-surface.fragment.rgb * ubo.exposure);
|
||||
#endif
|
||||
#if GAMMA_CORRECT
|
||||
surface.fragment.rgb = pow(surface.fragment.rgb, vec3(1.0 / ubo.gamma));
|
||||
#endif
|
||||
#if WHITENOISE
|
||||
if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) whitenoise(surface.fragment.rgb, ubo.mode.parameters);
|
||||
#endif
|
||||
|
||||
outFragColor = vec4(surface.fragment.rgb,1);
|
||||
}
|
||||
|
||||
void populateSurface() {
|
||||
surface.pass = inPushConstantPass;
|
||||
{
|
||||
#if !MULTISAMPLING
|
||||
const float depth = subpassLoad(samplerDepth).r;
|
||||
#else
|
||||
const float depth = resolve(samplerDepth, ubo.msaa).r;
|
||||
#endif
|
||||
|
||||
vec4 positionEye = ubo.matrices.iProjection[surface.pass] * vec4(inUv * 2.0 - 1.0, depth, 1.0);
|
||||
positionEye /= positionEye.w;
|
||||
surface.position.eye = positionEye.xyz;
|
||||
surface.position.world = vec3( ubo.matrices.iView[surface.pass] * positionEye );
|
||||
}
|
||||
#if 0
|
||||
{
|
||||
const vec4 near4 = ubo.matrices.iProjectionView[surface.pass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = ubo.matrices.iProjectionView[surface.pass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
surface.ray.origin = near3;
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
}
|
||||
// separate our ray direction due to floating point precision problems
|
||||
{
|
||||
const mat4 iProjectionView = inverse( ubo.matrices.projection[surface.pass] * mat4(mat3(ubo.matrices.view[surface.pass])) );
|
||||
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
}
|
||||
#else
|
||||
{
|
||||
const mat4 iProjectionView = inverse( ubo.matrices.projection[surface.pass] * mat4(mat3(ubo.matrices.view[surface.pass])) );
|
||||
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
surface.ray.origin = ubo.matrices.eyePos[surface.pass].xyz;
|
||||
}
|
||||
#endif
|
||||
#if !MULTISAMPLING
|
||||
surface.normal.world = decodeNormals( subpassLoad(samplerNormal).xy );
|
||||
const uvec2 ID = subpassLoad(samplerId).xy;
|
||||
#else
|
||||
surface.normal.world = decodeNormals( resolve(samplerNormal, ubo.msaa).xy );
|
||||
const uvec2 ID = subpassLoad(samplerId, 0).xy; //resolve(samplerId, ubo.msaa).xy;
|
||||
#endif
|
||||
surface.normal.eye = vec3( ubo.matrices.view[surface.pass] * vec4(surface.normal.world, 0.0) );
|
||||
|
||||
if ( ID.x == 0 || ID.y == 0 ) {
|
||||
surface.fragment.rgb = texture( samplerSkybox, surface.ray.direction ).rgb;
|
||||
surface.fragment.a = 0.0;
|
||||
postProcess();
|
||||
return;
|
||||
}
|
||||
const uint drawId = ID.x - 1;
|
||||
const DrawCall drawCall = drawCalls[drawId];
|
||||
surface.material.id = ID.y + drawCall.materialIndex - 1;
|
||||
const Material material = materials[surface.material.id];
|
||||
surface.material.albedo = material.colorBase;
|
||||
surface.fragment = material.colorEmissive;
|
||||
#if DEFERRED_SAMPLING
|
||||
#if !MULTISAMPLING
|
||||
surface.uv = subpassLoad(samplerUv).xy;
|
||||
#else
|
||||
surface.uv = resolve(samplerUv, ubo.msaa).xy;
|
||||
#endif
|
||||
const float mip = mipLevel(inUv.xy);
|
||||
const bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
|
||||
Texture textureAtlas;
|
||||
if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas];
|
||||
if ( validTextureIndex( drawCall.textureIndex + material.indexAlbedo ) ) {
|
||||
const Texture t = textures[drawCall.textureIndex + material.indexAlbedo];
|
||||
surface.material.albedo = textureLod( samplerTextures[nonuniformEXT((useAtlas)?textureAtlas.index:t.index)], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, surface.uv ) : surface.uv, mip );
|
||||
}
|
||||
// OPAQUE
|
||||
if ( material.modeAlpha == 0 ) {
|
||||
surface.material.albedo.a = 1;
|
||||
// BLEND
|
||||
} else if ( material.modeAlpha == 1 ) {
|
||||
|
||||
// MASK
|
||||
} else if ( material.modeAlpha == 2 ) {
|
||||
|
||||
}
|
||||
// Emissive textures
|
||||
if ( validTextureIndex( drawCall.textureIndex + material.indexEmissive ) ) {
|
||||
const Texture t = textures[drawCall.textureIndex + material.indexEmissive];
|
||||
surface.fragment += textureLod( samplerTextures[nonuniformEXT((useAtlas)?textureAtlas.index:t.index)], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, surface.uv ) : surface.uv, mip );
|
||||
}
|
||||
#else
|
||||
#if !MULTISAMPLING
|
||||
surface.material.albedo = subpassLoad(samplerAlbedo);
|
||||
#else
|
||||
surface.material.albedo = resolve(samplerAlbedo, ubo.msaa);
|
||||
#endif
|
||||
#endif
|
||||
surface.material.metallic = material.factorMetallic;
|
||||
surface.material.roughness = material.factorRoughness;
|
||||
surface.material.occlusion = material.factorOcclusion;
|
||||
surface.material.indexLightmap = material.indexLightmap;
|
||||
}
|
||||
15
bin/data/shaders/display/subpass.no-msaa.frag.glsl
Normal file
15
bin/data/shaders/display/subpass.no-msaa.frag.glsl
Normal file
@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define MULTISAMPLING 0
|
||||
#include "./subpass.h"
|
||||
|
||||
void main() {
|
||||
populateSurface();
|
||||
|
||||
const vec3 ambient = ubo.ambient.rgb * surface.material.occlusion;
|
||||
surface.fragment.rgb += (0 <= surface.material.indexLightmap) ? (surface.material.albedo.rgb + ambient) : (surface.material.albedo.rgb * ambient);
|
||||
|
||||
pbr();
|
||||
postProcess();
|
||||
}
|
||||
@ -1,559 +0,0 @@
|
||||
#version 450
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
|
||||
layout (constant_id = 0) const uint LIGHTS = 256;
|
||||
|
||||
layout (input_attachment_index = 0, binding = 1) uniform subpassInput samplerAlbedoMetallic;
|
||||
layout (input_attachment_index = 0, binding = 2) uniform subpassInput samplerNormalRoughness;
|
||||
layout (input_attachment_index = 0, binding = 3) uniform subpassInput samplerDepth;
|
||||
|
||||
layout (binding = 5) uniform sampler3D samplerNoise;
|
||||
layout (binding = 6) uniform sampler2D samplerShadows[LIGHTS];
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in flat uint inPushConstantPass;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
/*
|
||||
const vec2 poissonDisk[4] = vec2[](
|
||||
vec2( -0.94201624, -0.39906216 ),
|
||||
vec2( 0.94558609, -0.76890725 ),
|
||||
vec2( -0.094184101, -0.92938870 ),
|
||||
vec2( 0.34495938, 0.29387760 )
|
||||
);
|
||||
*/
|
||||
vec2 poissonDisk[16] = vec2[](
|
||||
vec2( -0.94201624, -0.39906216 ),
|
||||
vec2( 0.94558609, -0.76890725 ),
|
||||
vec2( -0.094184101, -0.92938870 ),
|
||||
vec2( 0.34495938, 0.29387760 ),
|
||||
vec2( -0.91588581, 0.45771432 ),
|
||||
vec2( -0.81544232, -0.87912464 ),
|
||||
vec2( -0.38277543, 0.27676845 ),
|
||||
vec2( 0.97484398, 0.75648379 ),
|
||||
vec2( 0.44323325, -0.97511554 ),
|
||||
vec2( 0.53742981, -0.47373420 ),
|
||||
vec2( -0.26496911, -0.41893023 ),
|
||||
vec2( 0.79197514, 0.19090188 ),
|
||||
vec2( -0.24188840, 0.99706507 ),
|
||||
vec2( -0.81409955, 0.91437590 ),
|
||||
vec2( 0.19984126, 0.78641367 ),
|
||||
vec2( 0.14383161, -0.14100790 )
|
||||
);
|
||||
|
||||
struct Light {
|
||||
vec3 position;
|
||||
float radius;
|
||||
|
||||
vec3 color;
|
||||
float power;
|
||||
|
||||
int type;
|
||||
float depthBias;
|
||||
float padding1;
|
||||
float padding2;
|
||||
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
};
|
||||
|
||||
struct Matrices {
|
||||
mat4 view[2];
|
||||
mat4 projection[2];
|
||||
};
|
||||
|
||||
struct Space {
|
||||
vec3 eye;
|
||||
vec3 world;
|
||||
} position, normal, view;
|
||||
|
||||
struct Fog {
|
||||
vec3 color;
|
||||
float stepScale;
|
||||
|
||||
vec3 offset;
|
||||
float densityScale;
|
||||
|
||||
float densityThreshold;
|
||||
float densityMultiplier;
|
||||
float absorbtion;
|
||||
float padding1;
|
||||
|
||||
vec2 range;
|
||||
float padding2;
|
||||
float padding3;
|
||||
};
|
||||
|
||||
struct Mode {
|
||||
uint type;
|
||||
uint scalar;
|
||||
vec2 padding;
|
||||
vec4 parameters;
|
||||
};
|
||||
|
||||
layout (binding = 0) uniform UBO {
|
||||
Matrices matrices;
|
||||
vec3 ambient;
|
||||
float kexp;
|
||||
Mode mode;
|
||||
Fog fog;
|
||||
Light lights[LIGHTS];
|
||||
} ubo;
|
||||
|
||||
void phong( Light light, vec4 albedoSpecular, inout vec3 i ) {
|
||||
vec3 Ls = vec3(1.0, 1.0, 1.0); // light specular
|
||||
vec3 Ld = light.color; // light color
|
||||
vec3 La = vec3(1.0, 1.0, 1.0);
|
||||
|
||||
vec3 Ks = vec3(albedoSpecular.a); // material specular
|
||||
vec3 Kd = albedoSpecular.rgb; // material diffuse
|
||||
vec3 Ka = vec3(1.0, 1.0, 1.0);
|
||||
|
||||
float Kexp = ubo.kexp;
|
||||
|
||||
vec3 V = position.eye;
|
||||
vec3 N = normal.eye;
|
||||
vec3 L = light.position.xyz - V;
|
||||
float dist = length(L);
|
||||
// if ( light.radius > 0.001 && light.radius < dist ) return;
|
||||
|
||||
vec3 D = normalize(L);
|
||||
float d_dot = max(dot( D, N ), 0.0);
|
||||
|
||||
vec3 R = reflect( -D, N );
|
||||
vec3 S = normalize(-V);
|
||||
float s_factor = pow( max(dot( R, S ), 0.0), Kexp );
|
||||
if ( Kexp < 0.0001 ) s_factor = 0;
|
||||
|
||||
float radiance = light.power / (dist * dist);
|
||||
|
||||
vec3 Ia = La * Ka;
|
||||
vec3 Id = Ld * Kd * d_dot * radiance;
|
||||
vec3 Is = Ls * Ks * s_factor * radiance;
|
||||
|
||||
i += Id + Is;
|
||||
}
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
float DistributionGGX(vec3 N, vec3 H, float roughness) {
|
||||
float a = roughness*roughness;
|
||||
float a2 = a*a;
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
float NdotH2 = NdotH*NdotH;
|
||||
|
||||
float num = a2;
|
||||
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
||||
denom = PI * denom * denom;
|
||||
|
||||
return num / denom;
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float NdotV, float roughness) {
|
||||
float r = (roughness + 1.0);
|
||||
float k = (r*r) / 8.0;
|
||||
|
||||
float num = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
|
||||
return num / denom;
|
||||
}
|
||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
float random(vec3 seed, int i){
|
||||
vec4 seed4 = vec4(seed,i);
|
||||
float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
|
||||
return fract(sin(dot_product) * 43758.5453);
|
||||
}
|
||||
|
||||
float shadowFactor( Light light, uint shadowMap ) {
|
||||
vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
|
||||
positionClip.xyz /= positionClip.w;
|
||||
|
||||
if ( positionClip.x < -1 || positionClip.x >= 1 ) return 0.0;
|
||||
if ( positionClip.y < -1 || positionClip.y >= 1 ) return 0.0;
|
||||
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return 0.0;
|
||||
|
||||
float factor = 1.0;
|
||||
|
||||
// spot light
|
||||
if ( light.type == -2 || light.type == -3 ) {
|
||||
float dist = length( positionClip.xy );
|
||||
if ( dist > 0.5 ) return 0.0;
|
||||
|
||||
// spot light with attenuation
|
||||
if ( light.type == -3 ) {
|
||||
factor = 1.0 - (pow(dist * 2,2.0));
|
||||
}
|
||||
}
|
||||
|
||||
vec2 uv = positionClip.xy * 0.5 + 0.5;
|
||||
float bias = light.depthBias;
|
||||
if ( !true ) {
|
||||
float cosTheta = clamp(dot(normal.eye, normalize(light.position.xyz - position.eye)), 0, 1);
|
||||
bias = clamp(bias * tan(acos(cosTheta)), 0, 0.01);
|
||||
} else if ( true ) {
|
||||
bias = max(bias * 10 * (1.0 - dot(normal.eye, normalize(light.position.xyz - position.eye))), bias);
|
||||
}
|
||||
|
||||
float eyeDepth = positionClip.z;
|
||||
int samples = poissonDisk.length();
|
||||
if ( samples <= 1 ) {
|
||||
return eyeDepth < texture(samplerShadows[shadowMap], uv).r - bias ? 0.0 : factor;
|
||||
}
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
// int index = i;
|
||||
// int index = int( float(samples) * random(gl_FragCoord.xyy, i) ) % samples;
|
||||
int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples;
|
||||
float lightDepth = texture(samplerShadows[shadowMap], uv + poissonDisk[index] / 700.0 ).r;
|
||||
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
vec3 hslToRgb(vec3 HSL) {
|
||||
vec3 RGB; {
|
||||
float H = HSL.x;
|
||||
float R = abs(H * 6 - 3) - 1;
|
||||
float G = 2 - abs(H * 6 - 2);
|
||||
float B = 2 - abs(H * 6 - 4);
|
||||
RGB = clamp(vec3(R,G,B), 0, 1);
|
||||
}
|
||||
float C = (1 - abs(2 * HSL.z - 1)) * HSL.y;
|
||||
return (RGB - 0.5) * C + HSL.z;
|
||||
}
|
||||
vec3 rgbToHsl(vec3 RGB) {
|
||||
float Epsilon = 1e-10;
|
||||
vec3 HCV; {
|
||||
vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
|
||||
vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
|
||||
float C = Q.x - min(Q.w, Q.y);
|
||||
float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z);
|
||||
HCV = vec3(H, C, Q.x);
|
||||
}
|
||||
float L = HCV.z - HCV.y * 0.5;
|
||||
float S = HCV.y / (1 - abs(L * 2 - 1) + Epsilon);
|
||||
return vec3(HCV.x, S, L);
|
||||
}
|
||||
|
||||
float hueDistance(float h1, float h2) {
|
||||
float diff = abs((h1 - h2));
|
||||
return min(abs((1.0 - diff)), diff);
|
||||
}
|
||||
const float lightnessSteps = 4.0;
|
||||
float lightnessStep(float l) {
|
||||
return floor((0.5 + l * lightnessSteps)) / lightnessSteps;
|
||||
}
|
||||
const int indexMatrix16x16[256] = int[](0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255,
|
||||
128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127,
|
||||
32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223,
|
||||
160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95,
|
||||
8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247,
|
||||
136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119,
|
||||
40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215,
|
||||
168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87,
|
||||
2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253,
|
||||
130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125,
|
||||
34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221,
|
||||
162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93,
|
||||
10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245,
|
||||
138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117,
|
||||
42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213,
|
||||
170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85);
|
||||
float indexValue16x16( float scale ) {
|
||||
int x = int(mod(gl_FragCoord.x * scale, 16));
|
||||
int y = int(mod(gl_FragCoord.y * scale, 16));
|
||||
return indexMatrix16x16[(x + y * 16)] / 256.0;
|
||||
}
|
||||
const int indexMatrix8x8[64] = int[](0, 32, 8, 40, 2, 34, 10, 42,
|
||||
48, 16, 56, 24, 50, 18, 58, 26,
|
||||
12, 44, 4, 36, 14, 46, 6, 38,
|
||||
60, 28, 52, 20, 62, 30, 54, 22,
|
||||
3, 35, 11, 43, 1, 33, 9, 41,
|
||||
51, 19, 59, 27, 49, 17, 57, 25,
|
||||
15, 47, 7, 39, 13, 45, 5, 37,
|
||||
63, 31, 55, 23, 61, 29, 53, 21);
|
||||
float indexValue8x8( float scale ) {
|
||||
int x = int(mod(gl_FragCoord.x * scale, 8));
|
||||
int y = int(mod(gl_FragCoord.y * scale, 8));
|
||||
return indexMatrix8x8[(x + y * 8)] / 64.0;
|
||||
}
|
||||
const int indexMatrix4x4[16] = int[](0, 8, 2, 10,
|
||||
12, 4, 14, 6,
|
||||
3, 11, 1, 9,
|
||||
15, 7, 13, 5);
|
||||
float indexValue4x4( float scale ) {
|
||||
int x = int(mod(gl_FragCoord.x * scale, 4));
|
||||
int y = int(mod(gl_FragCoord.y * scale, 4));
|
||||
return indexMatrix4x4[(x + y * 4)] / 16.0;
|
||||
}
|
||||
vec3[2] closestColors(float hue) {
|
||||
vec3 ret[2];
|
||||
vec3 closest = vec3(-2, 0, 0);
|
||||
vec3 secondClosest = vec3(-2, 0, 0);
|
||||
vec3 temp;
|
||||
/*
|
||||
for (int i = 0; i < palette.length(); ++i) {
|
||||
temp = rgbToHsl(palette[i].rgb);
|
||||
float tempDistance = hueDistance(temp.x, hue);
|
||||
if (tempDistance < hueDistance(closest.x, hue)) {
|
||||
secondClosest = closest;
|
||||
closest = temp;
|
||||
} else {
|
||||
if (tempDistance < hueDistance(secondClosest.x, hue)) {
|
||||
secondClosest = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
ret[0] = closest;
|
||||
ret[1] = secondClosest;
|
||||
return ret;
|
||||
}
|
||||
float dither(float color) {
|
||||
float closestColor = (color < 0.5) ? 0 : 1;
|
||||
float secondClosestColor = 1 - closestColor;
|
||||
float d = 1; // -0.5 - fract(ubo.mode.parameters.w / 8.0);
|
||||
if ( ubo.mode.scalar == 16 ) {
|
||||
d = indexValue16x16(1);
|
||||
} else if ( ubo.mode.scalar == 8 ) {
|
||||
d = indexValue8x8(1);
|
||||
} else if ( ubo.mode.scalar == 4 ) {
|
||||
d = indexValue4x4(1);
|
||||
}
|
||||
float distance = abs(closestColor - color);
|
||||
return (distance < d) ? closestColor : secondClosestColor;
|
||||
}
|
||||
void dither(inout vec3 color) {
|
||||
vec3 hsl = rgbToHsl(color);
|
||||
hsl.x = dither(hsl.x);
|
||||
color = hslToRgb(hsl);
|
||||
}
|
||||
void dither1(inout vec3 color) {
|
||||
vec3 hsl = rgbToHsl(color);
|
||||
|
||||
float d = 0;
|
||||
vec3 cs[2] = { hsl, hsl }; // closestColors(hsl.x);
|
||||
float scale = 1;
|
||||
if ( ubo.mode.scalar == 16 ) {
|
||||
d = indexValue16x16(scale);
|
||||
} else if ( ubo.mode.scalar == 8 ) {
|
||||
d = indexValue8x8(scale);
|
||||
} else if ( ubo.mode.scalar == 4 ) {
|
||||
d = indexValue4x4(scale);
|
||||
}
|
||||
float hueDiff = hueDistance(hsl.x, cs[0].x) / hueDistance(cs[1].x, cs[0].x);
|
||||
float l1 = lightnessStep(max((hsl.z - 0.125), 0.0));
|
||||
float l2 = lightnessStep(min((hsl.z + 0.124), 1.0));
|
||||
float lightnessDiff = (hsl.z - l1) / (l2 - l1);
|
||||
|
||||
vec3 resultColor = (hueDiff < d) ? cs[0] : cs[1];
|
||||
resultColor.z = (lightnessDiff < d) ? l1 : l2;
|
||||
color = hslToRgb(resultColor);
|
||||
}
|
||||
vec3 dither2() {
|
||||
vec3 vDither = dot( vec2( 171.0, 231.0 ), inUv.xy + ubo.mode.parameters.w ).xxx;
|
||||
vDither.rgb = fract( vDither.rgb / vec3( 103.0, 71.0, 97.0 ) ) - vec3( 0.5, 0.5, 0.5 );
|
||||
return ( vDither.rgb / 255.0 ) * 0.375;
|
||||
}
|
||||
|
||||
float rand2(vec2 co){
|
||||
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453);
|
||||
}
|
||||
float rand3(vec3 co){
|
||||
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
|
||||
}
|
||||
void whitenoise(inout vec3 color) {
|
||||
float flicker = ubo.mode.parameters.x;
|
||||
float pieces = ubo.mode.parameters.y;
|
||||
float blend = ubo.mode.parameters.z;
|
||||
float time = ubo.mode.parameters.w;
|
||||
if ( blend < 0.0001 ) return;
|
||||
float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
|
||||
// float whiteNoise = rand3( floor(position.world / pieces) + floor(time * 2) );
|
||||
float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
|
||||
color = mix( color, vec3(whiteNoise), blend );
|
||||
}
|
||||
|
||||
void pbr( Light light, vec3 albedo, float metallic, float roughness, vec3 lightPositionWorld, inout vec3 i ) {
|
||||
vec3 F0 = vec3(0.04);
|
||||
F0 = mix(F0, albedo, metallic);
|
||||
|
||||
vec3 N = normalize(normal.eye);
|
||||
vec3 L = light.position.xyz - position.eye;
|
||||
float dist = length(L);
|
||||
// if ( light.radius > 0.001 && light.radius < dist ) return;
|
||||
|
||||
vec3 D = normalize(L);
|
||||
vec3 V = normalize(-position.eye);
|
||||
vec3 H = normalize(V + D);
|
||||
|
||||
float NdotD = max(dot(N, D), 0.0);
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
|
||||
vec3 radiance = light.color.rgb * light.power / (dist * dist);
|
||||
/*
|
||||
if ( light.radius > 0.0001 ) {
|
||||
radiance *= clamp( light.radius / (pow(dist, 2.0) + 1.0), 0.0, 1.0 );
|
||||
} else if ( false ) {
|
||||
radiance /= dist * dist;
|
||||
}
|
||||
*/
|
||||
|
||||
// cook-torrance brdf
|
||||
float NDF = DistributionGGX(N, H, roughness);
|
||||
float G = GeometrySmith(N, V, D, roughness);
|
||||
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
|
||||
|
||||
vec3 kS = F;
|
||||
vec3 kD = vec3(1.0) - kS;
|
||||
kD *= 1.0 - metallic;
|
||||
|
||||
vec3 numerator = NDF * G * F;
|
||||
float denominator = 4.0 * NdotV * NdotD;
|
||||
vec3 specular = numerator / max(denominator, 0.001);
|
||||
|
||||
// add to outgoing radiance Lo
|
||||
i += (kD * albedo / PI + specular) * radiance * NdotD;
|
||||
}
|
||||
|
||||
vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, vec3 rayOrigin, vec3 rayDir ) {
|
||||
vec3 t0 = (boundsMin - rayOrigin) / rayDir;
|
||||
vec3 t1 = (boundsMax - rayOrigin) / rayDir;
|
||||
vec3 tmin = min(t0, t1);
|
||||
vec3 tmax = max(t0, t1);
|
||||
float dstA = max( max(tmin.x, tmin.y), tmin.z );
|
||||
float dstB = min( tmax.x, min(tmax.y, tmax.z) );
|
||||
float dstToBox = max(0, dstA);
|
||||
float dstInsideBox = max(0, dstB - dstToBox);
|
||||
return vec2(dstToBox, dstInsideBox);
|
||||
}
|
||||
|
||||
float sampleDensity( vec3 position ) {
|
||||
vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
|
||||
return max(0, texture(samplerNoise, uvw).r - ubo.fog.densityThreshold) * ubo.fog.densityMultiplier;
|
||||
}
|
||||
|
||||
void fog( inout vec3 i, float scale ) {
|
||||
if ( ubo.fog.stepScale <= 0 ) return;
|
||||
if ( ubo.fog.range.x == 0 || ubo.fog.range.y == 0 ) return;
|
||||
|
||||
mat4 iProjView = inverse( ubo.matrices.projection[inPushConstantPass] * ubo.matrices.view[inPushConstantPass] );
|
||||
vec4 near4 = iProjView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
vec4 far4 = iProjView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
vec3 near3 = near4.xyz / near4.w;
|
||||
vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
vec3 rayOrigin = near3;
|
||||
vec3 rayDir = normalize( far3 - near3 );
|
||||
|
||||
float range = ubo.fog.range.y;
|
||||
vec3 boundsMin = vec3(-range,-range,-range) + rayOrigin;
|
||||
vec3 boundsMax = vec3(range,range,range) + rayOrigin;
|
||||
int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
|
||||
|
||||
vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayOrigin, rayDir );
|
||||
float dstToBox = rayBoxInfo.x;
|
||||
float dstInsideBox = rayBoxInfo.y;
|
||||
float depth = position.eye.z;
|
||||
|
||||
float lightEnergy = 0;
|
||||
// march
|
||||
if ( 0 <= dstInsideBox && dstToBox <= depth ) {
|
||||
float dstTravelled = 0;
|
||||
float stepSize = dstInsideBox / numSteps;
|
||||
float dstLimit = min( depth - dstToBox, dstInsideBox );
|
||||
float totalDensity = 0;
|
||||
float transmittance = 1;
|
||||
while ( dstTravelled < dstLimit ) {
|
||||
vec3 rayPos = rayOrigin + rayDir * (dstToBox + dstTravelled);
|
||||
float density = sampleDensity(rayPos);
|
||||
if ( density > 0 ) {
|
||||
transmittance *= exp(-density * stepSize * ubo.fog.absorbtion);
|
||||
if ( transmittance < 0.01 ) break;
|
||||
}
|
||||
dstTravelled += stepSize;
|
||||
}
|
||||
i.rgb = mix(ubo.fog.color.rgb, i.rgb, transmittance);
|
||||
}
|
||||
|
||||
vec3 color = ubo.fog.color.rgb;
|
||||
float inner = ubo.fog.range.x;
|
||||
float outer = ubo.fog.range.y * scale;
|
||||
float distance = length(-position.eye);
|
||||
float factor = (distance - inner) / (outer - inner);
|
||||
factor = clamp( factor, 0.0, 1.0 );
|
||||
|
||||
i.rgb = mix(i.rgb, color, factor);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 albedoMetallic = subpassLoad(samplerAlbedoMetallic);
|
||||
vec4 normalRoughness = subpassLoad(samplerNormalRoughness);
|
||||
// vec4 positionAO = subpassLoad(samplerPositionAO);
|
||||
|
||||
normal.eye = normalRoughness.rgb;
|
||||
{
|
||||
mat4 iProj = inverse( ubo.matrices.projection[inPushConstantPass] );
|
||||
mat4 iView = inverse( ubo.matrices.view[inPushConstantPass] );
|
||||
float depth = subpassLoad(samplerDepth).r;
|
||||
|
||||
vec4 positionClip = vec4(inUv * 2.0 - 1.0, depth, 1.0);
|
||||
vec4 positionEye = iProj * positionClip;
|
||||
positionEye /= positionEye.w;
|
||||
position.eye = positionEye.xyz;
|
||||
|
||||
vec4 positionWorld = iView * positionEye;
|
||||
position.world = positionWorld.xyz;
|
||||
}
|
||||
|
||||
bool usePbr = true;
|
||||
bool gammaCorrect = false;
|
||||
float litFactor = 1.0;
|
||||
float ao = 1; // positionAO.a;
|
||||
vec3 fragColor = albedoMetallic.rgb * ubo.ambient.rgb * ao;
|
||||
for ( uint i = 0; i < LIGHTS; ++i ) {
|
||||
Light light = ubo.lights[i];
|
||||
|
||||
if ( light.power <= 0.001 ) continue;
|
||||
vec3 lightPositionWorld = light.position.xyz;
|
||||
light.position.xyz = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1));
|
||||
if ( light.type < 0 ) {
|
||||
float factor = shadowFactor( light, i );
|
||||
if ( factor <= 0.0001 ) continue;
|
||||
light.power *= factor;
|
||||
litFactor += light.power;
|
||||
}
|
||||
if ( usePbr ) {
|
||||
pbr( light, albedoMetallic.rgb, albedoMetallic.a, normalRoughness.a, lightPositionWorld, fragColor );
|
||||
} else
|
||||
phong( light, albedoMetallic, fragColor );
|
||||
}
|
||||
|
||||
if ( gammaCorrect ) {
|
||||
fragColor = fragColor / (fragColor + vec3(1.0));
|
||||
fragColor = pow(fragColor, vec3(1.0/2.2));
|
||||
}
|
||||
|
||||
fog(fragColor, litFactor);
|
||||
|
||||
/*
|
||||
if ( (ubo.mode.type & (0x1 << 0)) == (0x1 << 0) ) {
|
||||
//dither1(fragColor);
|
||||
fragColor += dither2();
|
||||
}
|
||||
if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) {
|
||||
whitenoise(fragColor);
|
||||
}
|
||||
*/
|
||||
outFragColor = vec4(fragColor,1);
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (location = 0) in vec2 inPos;
|
||||
layout (location = 1) in vec2 inUv;
|
||||
|
||||
@ -1,820 +1,17 @@
|
||||
#version 450
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define MULTISAMPLING 1
|
||||
#define DEFERRED_SAMPLING 1
|
||||
#define VXGI 1
|
||||
|
||||
#define VXGI_NDC 1
|
||||
#define VXGI_SHADOWS 1
|
||||
|
||||
#define FOG 1
|
||||
#define FOG_RAY_MARCH 1
|
||||
|
||||
#define WHITENOISE 1
|
||||
#define GAMMA_CORRECT 1
|
||||
#define TONE_MAP 1
|
||||
|
||||
#define LAMBERT 0
|
||||
#define PBR 1
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
const float EPSILON = 0.00001;
|
||||
const float SQRT2 = 1.41421356237;
|
||||
|
||||
const float LIGHT_POWER_CUTOFF = 0.0005;
|
||||
|
||||
const vec2 poissonDisk[16] = vec2[](
|
||||
vec2( -0.94201624, -0.39906216 ),
|
||||
vec2( 0.94558609, -0.76890725 ),
|
||||
vec2( -0.094184101, -0.92938870 ),
|
||||
vec2( 0.34495938, 0.29387760 ),
|
||||
vec2( -0.91588581, 0.45771432 ),
|
||||
vec2( -0.81544232, -0.87912464 ),
|
||||
vec2( -0.38277543, 0.27676845 ),
|
||||
vec2( 0.97484398, 0.75648379 ),
|
||||
vec2( 0.44323325, -0.97511554 ),
|
||||
vec2( 0.53742981, -0.47373420 ),
|
||||
vec2( -0.26496911, -0.41893023 ),
|
||||
vec2( 0.79197514, 0.19090188 ),
|
||||
vec2( -0.24188840, 0.99706507 ),
|
||||
vec2( -0.81409955, 0.91437590 ),
|
||||
vec2( 0.19984126, 0.78641367 ),
|
||||
vec2( 0.14383161, -0.14100790 )
|
||||
);
|
||||
|
||||
layout (constant_id = 0) const uint CASCADES = 16;
|
||||
layout (constant_id = 1) const uint TEXTURES = 512;
|
||||
|
||||
struct Matrices {
|
||||
mat4 view[2];
|
||||
mat4 projection[2];
|
||||
mat4 iView[2];
|
||||
mat4 iProjection[2];
|
||||
mat4 iProjectionView[2];
|
||||
vec4 eyePos[2];
|
||||
mat4 vxgi;
|
||||
};
|
||||
|
||||
struct Ray {
|
||||
vec3 origin;
|
||||
vec3 direction;
|
||||
|
||||
vec3 position;
|
||||
float distance;
|
||||
};
|
||||
|
||||
struct Space {
|
||||
vec3 eye;
|
||||
vec3 world;
|
||||
};
|
||||
|
||||
struct Fog {
|
||||
vec3 color;
|
||||
float stepScale;
|
||||
|
||||
vec3 offset;
|
||||
float densityScale;
|
||||
|
||||
float densityThreshold;
|
||||
float densityMultiplier;
|
||||
float absorbtion;
|
||||
float padding1;
|
||||
|
||||
vec2 range;
|
||||
float padding2;
|
||||
float padding3;
|
||||
};
|
||||
|
||||
struct Mode {
|
||||
uint type;
|
||||
uint scalar;
|
||||
vec2 padding;
|
||||
vec4 parameters;
|
||||
};
|
||||
|
||||
struct Light {
|
||||
vec3 position;
|
||||
float radius;
|
||||
|
||||
vec3 color;
|
||||
float power;
|
||||
|
||||
int type;
|
||||
int mapIndex;
|
||||
float depthBias;
|
||||
float padding;
|
||||
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
};
|
||||
|
||||
struct Material {
|
||||
vec4 colorBase;
|
||||
vec4 colorEmissive;
|
||||
|
||||
float factorMetallic;
|
||||
float factorRoughness;
|
||||
float factorOcclusion;
|
||||
float factorAlphaCutoff;
|
||||
|
||||
int indexAlbedo;
|
||||
int indexNormal;
|
||||
int indexEmissive;
|
||||
int indexOcclusion;
|
||||
|
||||
int indexMetallicRoughness;
|
||||
int indexAtlas;
|
||||
int indexLightmap;
|
||||
int modeAlpha;
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
int index;
|
||||
int samp;
|
||||
int remap;
|
||||
float blend;
|
||||
|
||||
vec4 lerp;
|
||||
};
|
||||
|
||||
struct DrawCall {
|
||||
int materialIndex;
|
||||
uint materials;
|
||||
int textureIndex;
|
||||
uint textures;
|
||||
};
|
||||
|
||||
struct SurfaceMaterial {
|
||||
uint id;
|
||||
|
||||
vec4 albedo;
|
||||
vec4 indirect;
|
||||
|
||||
float metallic;
|
||||
float roughness;
|
||||
float occlusion;
|
||||
};
|
||||
|
||||
struct Surface {
|
||||
vec2 uv;
|
||||
Space position;
|
||||
Space normal;
|
||||
|
||||
Ray ray;
|
||||
|
||||
SurfaceMaterial material;
|
||||
|
||||
vec4 fragment;
|
||||
} surface;
|
||||
|
||||
struct Voxel {
|
||||
uvec2 id;
|
||||
vec3 position;
|
||||
vec3 normal;
|
||||
vec2 uv;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
#if !MULTISAMPLING
|
||||
layout (input_attachment_index = 0, binding = 0) uniform usubpassInput samplerId;
|
||||
layout (input_attachment_index = 1, binding = 1) uniform subpassInput samplerNormal;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerUv;
|
||||
#else
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerAlbedo;
|
||||
#endif
|
||||
layout (input_attachment_index = 3, binding = 3) uniform subpassInput samplerDepth;
|
||||
#else
|
||||
layout (input_attachment_index = 0, binding = 0) uniform usubpassInputMS samplerId;
|
||||
layout (input_attachment_index = 1, binding = 1) uniform subpassInputMS samplerNormal;
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerUv;
|
||||
#else
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerAlbedo;
|
||||
#endif
|
||||
layout (input_attachment_index = 3, binding = 3) uniform subpassInputMS samplerDepth;
|
||||
#endif
|
||||
|
||||
layout (binding = 4) uniform UBO {
|
||||
Matrices matrices;
|
||||
|
||||
Mode mode;
|
||||
Fog fog;
|
||||
|
||||
uint lights;
|
||||
uint materials;
|
||||
uint textures;
|
||||
uint drawCalls;
|
||||
|
||||
vec3 ambient;
|
||||
float gamma;
|
||||
|
||||
float exposure;
|
||||
uint msaa;
|
||||
uint shadowSamples;
|
||||
float cascadePower;
|
||||
} ubo;
|
||||
|
||||
layout (std140, binding = 5) readonly buffer Lights {
|
||||
Light lights[];
|
||||
};
|
||||
layout (std140, binding = 6) readonly buffer Materials {
|
||||
Material materials[];
|
||||
};
|
||||
layout (std140, binding = 7) readonly buffer Textures {
|
||||
Texture textures[];
|
||||
};
|
||||
layout (std140, binding = 8) readonly buffer DrawCalls {
|
||||
DrawCall drawCalls[];
|
||||
};
|
||||
|
||||
layout (binding = 9) uniform usampler3D voxelId[CASCADES];
|
||||
layout (binding = 10) uniform sampler3D voxelUv[CASCADES];
|
||||
layout (binding = 11) uniform sampler3D voxelNormal[CASCADES];
|
||||
layout (binding = 12) uniform sampler3D voxelRadiance[CASCADES];
|
||||
|
||||
layout (binding = 13) uniform sampler3D samplerNoise;
|
||||
layout (binding = 14) uniform samplerCube samplerSkybox;
|
||||
layout (binding = 15) uniform sampler2D samplerTextures[TEXTURES];
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in flat uint inPushConstantPass;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
// GGX/Towbridge-Reitz normal distribution function.
|
||||
// Uses Disney's reparametrization of alpha = roughness^2.
|
||||
float ndfGGX(float cosLh, float roughness) {
|
||||
const float alpha = roughness * roughness;
|
||||
const float alphaSq = alpha * alpha;
|
||||
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
|
||||
return alphaSq / (PI * denom * denom);
|
||||
}
|
||||
|
||||
// Single term for separable Schlick-GGX below.
|
||||
float gaSchlickG1(float cosTheta, float k) {
|
||||
return cosTheta / (cosTheta * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
|
||||
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
|
||||
const float r = roughness + 1.0;
|
||||
const float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
|
||||
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
|
||||
}
|
||||
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
float random(vec3 seed, int i){
|
||||
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
|
||||
}
|
||||
|
||||
// Returns a vector that is orthogonal to u.
|
||||
vec3 orthogonal(vec3 u){
|
||||
u = normalize(u);
|
||||
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
|
||||
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
|
||||
}
|
||||
float rand2(vec2 co){
|
||||
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453);
|
||||
}
|
||||
float rand3(vec3 co){
|
||||
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
|
||||
}
|
||||
void whitenoise(inout vec3 color) {
|
||||
const float flicker = ubo.mode.parameters.x;
|
||||
const float pieces = ubo.mode.parameters.y;
|
||||
const float blend = ubo.mode.parameters.z;
|
||||
const float time = ubo.mode.parameters.w;
|
||||
if ( blend < 0.0001 ) return;
|
||||
const float freq = sin(pow(mod(time, flicker) + flicker, 1.9));
|
||||
const float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) );
|
||||
color = mix( color, vec3(whiteNoise), blend );
|
||||
}
|
||||
vec3 gamma( vec3 i ) {
|
||||
return pow(i.rgb, vec3(1.0 / 2.2));
|
||||
}
|
||||
|
||||
vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, in Ray ray ) {
|
||||
const vec3 t0 = (boundsMin - ray.origin) / ray.direction;
|
||||
const vec3 t1 = (boundsMax - ray.origin) / ray.direction;
|
||||
const vec3 tmin = min(t0, t1);
|
||||
const vec3 tmax = max(t0, t1);
|
||||
const float tStart = max(0, max( max(tmin.x, tmin.y), tmin.z ));
|
||||
const float tEnd = max(0, min( tmax.x, min(tmax.y, tmax.z) ) - tStart);
|
||||
return vec2(tStart, tEnd);
|
||||
}
|
||||
|
||||
float sampleDensity( vec3 position ) {
|
||||
const vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01;
|
||||
return max(0, texture(samplerNoise, uvw).r - ubo.fog.densityThreshold) * ubo.fog.densityMultiplier;
|
||||
}
|
||||
|
||||
void fog( in Ray ray, inout vec3 i, float scale ) {
|
||||
if ( ubo.fog.stepScale <= 0 ) return;
|
||||
if ( ubo.fog.range.x == 0 || ubo.fog.range.y == 0 ) return;
|
||||
|
||||
#if FOG_RAY_MARCH
|
||||
const float range = ubo.fog.range.y;
|
||||
const vec3 boundsMin = vec3(-range,-range,-range) + ray.origin;
|
||||
const vec3 boundsMax = vec3(range,range,range) + ray.origin;
|
||||
const int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale );
|
||||
|
||||
const vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, ray );
|
||||
const float dstToBox = rayBoxInfo.x;
|
||||
const float dstInsideBox = rayBoxInfo.y;
|
||||
const float depth = surface.position.eye.z;
|
||||
|
||||
float lightEnergy = 0;
|
||||
// march
|
||||
if ( 0 <= dstInsideBox && dstToBox <= depth ) {
|
||||
float dstTravelled = 0;
|
||||
float stepSize = dstInsideBox / numSteps;
|
||||
float dstLimit = min( depth - dstToBox, dstInsideBox );
|
||||
float totalDensity = 0;
|
||||
float transmittance = 1;
|
||||
while ( dstTravelled < dstLimit ) {
|
||||
vec3 rayPos = ray.origin + ray.direction * (dstToBox + dstTravelled);
|
||||
float density = sampleDensity(rayPos);
|
||||
if ( density > 0 ) {
|
||||
transmittance *= exp(-density * stepSize * ubo.fog.absorbtion);
|
||||
if ( transmittance < 0.01 ) break;
|
||||
}
|
||||
dstTravelled += stepSize;
|
||||
}
|
||||
i.rgb = mix(ubo.fog.color.rgb, i.rgb, transmittance);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
const vec3 color = ubo.fog.color.rgb;
|
||||
const float inner = ubo.fog.range.x;
|
||||
const float outer = ubo.fog.range.y * scale;
|
||||
const float distance = length(-surface.position.eye);
|
||||
const float factor = clamp( (distance - inner) / (outer - inner), 0.0, 1.0 );
|
||||
|
||||
i.rgb = mix(i.rgb, color, factor);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 decodeNormals( vec2 enc ) {
|
||||
const vec2 ang = enc*2-1;
|
||||
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
|
||||
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
|
||||
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
|
||||
}
|
||||
float wrap( float i ) {
|
||||
return fract(i);
|
||||
}
|
||||
vec2 wrap( vec2 uv ) {
|
||||
return vec2( wrap( uv.x ), wrap( uv.y ) );
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
const vec2 dx_vtc = dFdx(uv);
|
||||
const vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
bool validTextureIndex( int textureIndex ) {
|
||||
return 0 <= textureIndex; // && textureIndex < ubo.textures;
|
||||
}
|
||||
vec4 resolve( subpassInputMS t ) {
|
||||
const int samples = int(ubo.msaa);
|
||||
vec4 resolved = vec4(0);
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
resolved += subpassLoad(t, i);
|
||||
}
|
||||
resolved /= vec4(samples);
|
||||
return resolved;
|
||||
}
|
||||
uvec4 resolve( usubpassInputMS t ) {
|
||||
const int samples = int(ubo.msaa);
|
||||
uvec4 resolved = uvec4(0);
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
resolved += subpassLoad(t, i);
|
||||
}
|
||||
resolved /= uvec4(samples);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
#if !VXGI_SHADOWS
|
||||
float shadowFactor( const Light light, float def ) {
|
||||
if ( !validTextureIndex(light.mapIndex) ) return 1.0;
|
||||
|
||||
vec4 positionClip = light.projection * light.view * vec4(surface.position.world, 1.0);
|
||||
positionClip.xyz /= positionClip.w;
|
||||
|
||||
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
|
||||
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
|
||||
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
|
||||
|
||||
float factor = 1.0;
|
||||
|
||||
// spot light
|
||||
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
|
||||
const float dist = length( positionClip.xy );
|
||||
if ( dist > 0.5 ) return def; //0.0;
|
||||
|
||||
// spot light with attenuation
|
||||
if ( abs(light.type) == 3 ) {
|
||||
factor = 1.0 - (pow(dist * 2,2.0));
|
||||
}
|
||||
}
|
||||
|
||||
const vec2 uv = positionClip.xy * 0.5 + 0.5;
|
||||
const float bias = light.depthBias;
|
||||
const float eyeDepth = positionClip.z;
|
||||
const int samples = int(ubo.shadowSamples);
|
||||
if ( samples < 1 ) return eyeDepth < texture(samplerTextures[nonuniformEXT(light.mapIndex)], uv).r - bias ? 0.0 : factor;
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
const int index = int( float(samples) * random(floor(surface.position.world.xyz * 1000.0), i)) % samples;
|
||||
const float lightDepth = texture(samplerTextures[nonuniformEXT(light.mapIndex)], uv + poissonDisk[index] / 700.0 ).r;
|
||||
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 postProcess() {
|
||||
#if FOG
|
||||
fog( surface.ray, surface.fragment.rgb, surface.fragment.a );
|
||||
#endif
|
||||
#if TONE_MAP
|
||||
surface.fragment.rgb = vec3(1.0) - exp(-surface.fragment.rgb * ubo.exposure);
|
||||
#endif
|
||||
#if GAMMA_CORRECT
|
||||
surface.fragment.rgb = pow(surface.fragment.rgb, vec3(1.0 / ubo.gamma));
|
||||
#endif
|
||||
#if WHITENOISE
|
||||
if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) whitenoise(surface.fragment.rgb);
|
||||
#endif
|
||||
|
||||
return vec4(surface.fragment.rgb,1);
|
||||
}
|
||||
|
||||
struct VoxelInfo {
|
||||
vec3 min;
|
||||
vec3 max;
|
||||
|
||||
float mipmapLevels;
|
||||
float radianceSize;
|
||||
float radianceSizeRecip;
|
||||
} voxelInfo;
|
||||
|
||||
uint BIASED_ROUND( float x ) {
|
||||
return uint( (x < 1 - voxelInfo.radianceSizeRecip) ? floor(x) : ceil(x));
|
||||
}
|
||||
float SCALE_CASCADE( uint x ) {
|
||||
return pow(1 + x, ubo.cascadePower);
|
||||
}
|
||||
|
||||
vec4 voxelTrace( inout Ray ray, float aperture, float maxDistance ) {
|
||||
uint CASCADE = 0;
|
||||
#if VXGI_NDC
|
||||
ray.origin = vec3( ubo.matrices.vxgi * vec4( ray.origin, 1.0 ) );
|
||||
ray.direction = vec3( ubo.matrices.vxgi * vec4( ray.direction, 0.0 ) );
|
||||
|
||||
CASCADE = BIASED_ROUND(clamp( max( max( abs(ray.origin.x), abs(ray.origin.y) ), abs(ray.origin.z) ), 0, CASCADES - 1 ));
|
||||
#else
|
||||
{
|
||||
vec3 uvw = vec3( ubo.matrices.vxgi * vec4( ray.origin, 1.0 ) );
|
||||
CASCADE = BIASED_ROUND(clamp( max( max( abs(uvw.x), abs(uvw.y) ), abs(uvw.z) ), 0, CASCADES - 1 ));
|
||||
}
|
||||
#endif
|
||||
const float granularityRecip = 12.0f;
|
||||
const float granularity = 1.0f / granularityRecip;
|
||||
const float occlusionFalloff = 128.0f;
|
||||
|
||||
const vec3 voxelBounds = voxelInfo.max - voxelInfo.min;
|
||||
const vec3 voxelBoundsRecip = 1.0f / voxelBounds;
|
||||
|
||||
const float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
const uint maxSteps = uint(voxelInfo.radianceSize * CASCADES * granularityRecip);
|
||||
// box
|
||||
const vec2 rayBoxInfoA = rayBoxDst( voxelInfo.min * SCALE_CASCADE(CASCADE), voxelInfo.max * SCALE_CASCADE(CASCADE), ray );
|
||||
const vec2 rayBoxInfoB = rayBoxDst( voxelInfo.min * SCALE_CASCADE(CASCADES), voxelInfo.max * SCALE_CASCADE(CASCADES), ray );
|
||||
|
||||
const float tStart = rayBoxInfoA.x;
|
||||
const float tEnd = maxDistance > 0 ? min(maxDistance, rayBoxInfoB.y * SCALE_CASCADE(CASCADES)) : rayBoxInfoB.y * SCALE_CASCADE(CASCADES);
|
||||
const float tDelta = voxelInfo.radianceSizeRecip * granularityRecip;
|
||||
// marcher
|
||||
ray.distance = tStart + tDelta * 4 * (1 + CASCADE);
|
||||
ray.position = vec3(0);
|
||||
|
||||
vec4 radiance = vec4(0);
|
||||
vec3 uvw = vec3(0);
|
||||
|
||||
float coneDiameter = coneCoefficient * ray.distance;
|
||||
float level = aperture > 0 ? log2( coneDiameter ) : 0;
|
||||
|
||||
vec4 color = vec4(0);
|
||||
float occlusion = 0.0;
|
||||
uint stepCounter = 0;
|
||||
#if VXGI_NDC
|
||||
while ( ray.distance < tEnd && color.a < 1.0 && occlusion < 1.0 && stepCounter++ < maxSteps ) {
|
||||
#else
|
||||
while ( color.a < 1.0 && stepCounter++ < maxSteps ) {
|
||||
#endif
|
||||
ray.distance += tDelta * (aperture > 0 ? coneDiameter : 1);
|
||||
ray.position = ray.origin + ray.direction * ray.distance;
|
||||
|
||||
#if VXGI_NDC
|
||||
uvw = ray.position;
|
||||
#else
|
||||
uvw = vec3( ubo.matrices.vxgi * vec4( ray.position, 1.0 ) );
|
||||
#endif
|
||||
CASCADE = BIASED_ROUND(clamp( max( max( abs(uvw.x), abs(uvw.y) ), abs(uvw.z) ), 0, CASCADES - 1 ));
|
||||
|
||||
uvw = (uvw / SCALE_CASCADE(CASCADE)) * 0.5 + 0.5;
|
||||
|
||||
if ( CASCADE >= CASCADES || uvw.x < 0.0 || uvw.y < 0.0 || uvw.z < 0.0 || uvw.x >= 1.0 || uvw.y >= 1.0 || uvw.z >= 1.0 ) break;
|
||||
|
||||
coneDiameter = coneCoefficient * ray.distance;
|
||||
level = aperture > 0 ? log2( coneDiameter ) : 0;
|
||||
|
||||
if ( level >= voxelInfo.mipmapLevels ) break;
|
||||
|
||||
radiance = textureLod(voxelRadiance[nonuniformEXT(CASCADE)], uvw.xzy, level);
|
||||
|
||||
color += (1.0 - color.a) * radiance;
|
||||
occlusion += ((1.0f - occlusion) * radiance.a) / (1.0f + occlusionFalloff * coneDiameter);
|
||||
}
|
||||
return maxDistance > 0 ? color : vec4(color.rgb, occlusion);
|
||||
// return vec4(color.rgb, occlusion);
|
||||
}
|
||||
vec4 voxelConeTrace( inout Ray ray, float aperture ) {
|
||||
return voxelTrace( ray, aperture, 0 );
|
||||
}
|
||||
vec4 voxelTrace( inout Ray ray, float maxDistance ) {
|
||||
return voxelTrace( ray, 0, maxDistance );
|
||||
}
|
||||
|
||||
#if VXGI_SHADOWS
|
||||
float shadowFactor( const Light light, float def ) {
|
||||
const float SHADOW_APERTURE = 0.2;
|
||||
const float DEPTH_BIAS = 0.0;
|
||||
|
||||
Ray ray;
|
||||
ray.direction = normalize( light.position - surface.position.world );
|
||||
ray.origin = surface.position.world + ray.direction * 0.5;
|
||||
float z = distance( surface.position.world, light.position ) - DEPTH_BIAS;
|
||||
return 1.0 - voxelTrace( ray, SHADOW_APERTURE, z ).a;
|
||||
}
|
||||
#endif
|
||||
#include "./subpass.h"
|
||||
|
||||
void main() {
|
||||
{
|
||||
#if !MULTISAMPLING
|
||||
const float depth = subpassLoad(samplerDepth).r;
|
||||
#else
|
||||
const float depth = resolve( samplerDepth ).r;
|
||||
#endif
|
||||
populateSurface();
|
||||
indirectLighting();
|
||||
|
||||
vec4 positionEye = ubo.matrices.iProjection[inPushConstantPass] * vec4(inUv * 2.0 - 1.0, depth, 1.0);
|
||||
positionEye /= positionEye.w;
|
||||
surface.position.eye = positionEye.xyz;
|
||||
surface.position.world = vec3( ubo.matrices.iView[inPushConstantPass] * positionEye );
|
||||
}
|
||||
#if 1
|
||||
{
|
||||
const vec4 near4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
const vec3 ambient = ubo.ambient.rgb * surface.material.occlusion + surface.material.indirect.rgb;
|
||||
surface.fragment.rgb += (0 <= surface.material.indexLightmap) ? (surface.material.albedo.rgb + ambient) : (surface.material.albedo.rgb * ambient);
|
||||
|
||||
surface.ray.origin = near3;
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
}
|
||||
// separate our ray direction due to floating point precision problems
|
||||
{
|
||||
const mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
|
||||
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
}
|
||||
#else
|
||||
{
|
||||
const mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) );
|
||||
const vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0));
|
||||
const vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0));
|
||||
const vec3 near3 = near4.xyz / near4.w;
|
||||
const vec3 far3 = far4.xyz / far4.w;
|
||||
|
||||
surface.ray.direction = normalize( far3 - near3 );
|
||||
surface.ray.origin = ubo.matrices.eyePos[inPushConstantPass].xyz;
|
||||
}
|
||||
#endif
|
||||
#if !MULTISAMPLING
|
||||
surface.normal.world = decodeNormals( subpassLoad(samplerNormal).xy );
|
||||
const uvec2 ID = subpassLoad(samplerId).xy;
|
||||
#else
|
||||
surface.normal.world = decodeNormals( resolve(samplerNormal).xy );
|
||||
const uvec2 ID = resolve(samplerId).xy;
|
||||
#endif
|
||||
surface.normal.eye = vec3( ubo.matrices.view[inPushConstantPass] * vec4(surface.normal.world, 0.0) );
|
||||
|
||||
if ( ID.x == 0 || ID.y == 0 ) {
|
||||
surface.fragment.rgb = texture( samplerSkybox, surface.ray.direction ).rgb;
|
||||
surface.fragment.a = 0.0;
|
||||
outFragColor = postProcess();
|
||||
return;
|
||||
}
|
||||
const uint drawId = ID.x - 1;
|
||||
const DrawCall drawCall = drawCalls[drawId];
|
||||
surface.material.id = ID.y + drawCall.materialIndex - 1;
|
||||
const Material material = materials[surface.material.id];
|
||||
surface.material.albedo = material.colorBase;
|
||||
surface.fragment = material.colorEmissive;
|
||||
#if DEFERRED_SAMPLING
|
||||
#if !MULTISAMPLING
|
||||
surface.uv = subpassLoad(samplerUv).xy;
|
||||
#else
|
||||
surface.uv = resolve(samplerUv).xy;
|
||||
#endif
|
||||
const float mip = mipLevel(inUv.xy);
|
||||
const bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas );
|
||||
Texture textureAtlas;
|
||||
if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas];
|
||||
if ( validTextureIndex( drawCall.textureIndex + material.indexAlbedo ) ) {
|
||||
const Texture t = textures[drawCall.textureIndex + material.indexAlbedo];
|
||||
surface.material.albedo = textureLod( samplerTextures[nonuniformEXT((useAtlas)?textureAtlas.index:t.index)], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, surface.uv ) : surface.uv, mip );
|
||||
}
|
||||
// OPAQUE
|
||||
if ( material.modeAlpha == 0 ) {
|
||||
surface.material.albedo.a = 1;
|
||||
// BLEND
|
||||
} else if ( material.modeAlpha == 1 ) {
|
||||
|
||||
// MASK
|
||||
} else if ( material.modeAlpha == 2 ) {
|
||||
|
||||
}
|
||||
// Emissive textures
|
||||
if ( validTextureIndex( drawCall.textureIndex + material.indexEmissive ) ) {
|
||||
const Texture t = textures[drawCall.textureIndex + material.indexEmissive];
|
||||
surface.fragment += textureLod( samplerTextures[nonuniformEXT((useAtlas)?textureAtlas.index:t.index)], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, surface.uv ) : surface.uv, mip );
|
||||
}
|
||||
#else
|
||||
#if !MULTISAMPLING
|
||||
surface.material.albedo = subpassLoad(samplerAlbedo);
|
||||
#else
|
||||
surface.material.albedo = resolve(samplerAlbedo);
|
||||
#endif
|
||||
#endif
|
||||
surface.material.metallic = material.factorMetallic;
|
||||
surface.material.roughness = material.factorRoughness;
|
||||
surface.material.occlusion = material.factorOcclusion;
|
||||
// GI
|
||||
{
|
||||
voxelInfo.radianceSize = textureSize( voxelRadiance[0], 0 ).x;
|
||||
voxelInfo.radianceSizeRecip = 1.0 / voxelInfo.radianceSize;
|
||||
voxelInfo.mipmapLevels = log2(voxelInfo.radianceSize) + 1;
|
||||
#if VXGI_NDC
|
||||
voxelInfo.min = vec3( -1 );
|
||||
voxelInfo.max = vec3( 1 );
|
||||
#else
|
||||
const mat4 inverseOrtho = inverse( ubo.matrices.vxgi );
|
||||
voxelInfo.min = vec3( inverseOrtho * vec4( -1, -1, -1, 1 ) );
|
||||
voxelInfo.max = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) );
|
||||
#endif
|
||||
}
|
||||
{
|
||||
const vec3 P = surface.position.world;
|
||||
const vec3 N = surface.normal.world;
|
||||
|
||||
#if 1
|
||||
const vec3 right = normalize(orthogonal(N));
|
||||
const vec3 up = normalize(cross(right, N));
|
||||
|
||||
const uint CONES_COUNT = 6;
|
||||
const vec3 CONES[] = {
|
||||
N,
|
||||
normalize(N + 0.0f * right + 0.866025f * up),
|
||||
normalize(N + 0.823639f * right + 0.267617f * up),
|
||||
normalize(N + 0.509037f * right + -0.7006629f * up),
|
||||
normalize(N + -0.50937f * right + -0.7006629f * up),
|
||||
normalize(N + -0.823639f * right + 0.267617f * up),
|
||||
};
|
||||
#else
|
||||
const vec3 ortho = normalize(orthogonal(N));
|
||||
const vec3 ortho2 = normalize(cross(ortho, N));
|
||||
|
||||
const vec3 corner = 0.5f * (ortho + ortho2);
|
||||
const vec3 corner2 = 0.5f * (ortho - ortho2);
|
||||
|
||||
const uint CONES_COUNT = 9;
|
||||
const vec3 CONES[] = {
|
||||
N,
|
||||
normalize(mix(N, ortho, 0.5)),
|
||||
normalize(mix(N, -ortho, 0.5)),
|
||||
normalize(mix(N, ortho2, 0.5)),
|
||||
normalize(mix(N, -ortho2, 0.5)),
|
||||
normalize(mix(N, corner, 0.5)),
|
||||
normalize(mix(N, -corner, 0.5)),
|
||||
normalize(mix(N, corner2, 0.5)),
|
||||
normalize(mix(N, -corner2, 0.5)),
|
||||
};
|
||||
#endif
|
||||
|
||||
const float DIFFUSE_CONE_APERTURE = 0.57735f;
|
||||
const float DIFFUSE_INDIRECT_FACTOR = 1.0f / float(CONES_COUNT);
|
||||
|
||||
const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * surface.material.roughness), 0.0174533f, PI); // tan( R * PI * 0.5f * 0.1f );
|
||||
const float SPECULAR_INDIRECT_FACTOR = (1.0 - surface.material.metallic) * 0.5; // 1.0f;
|
||||
|
||||
vec4 indirectDiffuse = vec4(0);
|
||||
vec4 indirectSpecular = vec4(0);
|
||||
|
||||
// outFragColor.rgb = voxelConeTrace( surface.ray, 0 ).rgb; return;
|
||||
|
||||
if ( DIFFUSE_INDIRECT_FACTOR > 0.0f ) {
|
||||
for ( uint i = 0; i < CONES_COUNT; ++i ) {
|
||||
float weight = i == 0 ? PI * 0.25f : PI * 0.15f;
|
||||
Ray ray;
|
||||
ray.direction = CONES[i].xyz;
|
||||
ray.origin = P; // + ray.direction;
|
||||
indirectDiffuse += voxelConeTrace( ray, DIFFUSE_CONE_APERTURE ) * weight;
|
||||
}
|
||||
surface.material.occlusion = 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0);
|
||||
// outFragColor.rgb = indirectDiffuse.rgb; return;
|
||||
// outFragColor.rgb = vec3(surface.material.occlusion); return;
|
||||
}
|
||||
if ( SPECULAR_INDIRECT_FACTOR > 0.0f ) {
|
||||
Ray ray;
|
||||
ray.direction = reflect( normalize(P - surface.ray.origin), N );
|
||||
ray.origin = P; // + ray.direction;
|
||||
indirectSpecular = voxelConeTrace( ray, SPECULAR_CONE_APERTURE );
|
||||
// outFragColor.rgb = indirectSpecular.rgb; return;
|
||||
}
|
||||
surface.material.indirect = indirectDiffuse * DIFFUSE_INDIRECT_FACTOR + indirectSpecular * SPECULAR_INDIRECT_FACTOR;
|
||||
// outFragColor.rgb = surface.material.indirect.rgb; return;
|
||||
}
|
||||
|
||||
if ( 0 <= material.indexLightmap ) {
|
||||
surface.fragment.rgb += surface.material.albedo.rgb + ubo.ambient.rgb * surface.material.occlusion + surface.material.indirect.rgb;
|
||||
} else {
|
||||
surface.fragment.rgb += surface.material.albedo.rgb * ubo.ambient.rgb * surface.material.occlusion + surface.material.indirect.rgb;
|
||||
}
|
||||
#if DEFERRED_SAMPLING
|
||||
// deferred sampling doesn't have a blended albedo buffer
|
||||
// in place we'll just cone trace behind the window
|
||||
if ( surface.material.albedo.a < 1.0 ) {
|
||||
Ray ray;
|
||||
ray.origin = surface.position.world;
|
||||
ray.direction = surface.ray.direction;
|
||||
vec4 radiance = voxelConeTrace( ray, surface.material.albedo.a );
|
||||
surface.fragment.rgb += (1.0 - surface.material.albedo.a) * radiance.rgb;
|
||||
}
|
||||
#endif
|
||||
|
||||
// corrections
|
||||
surface.material.roughness *= 4.0;
|
||||
|
||||
{
|
||||
const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic);
|
||||
const vec3 Lo = normalize( -surface.position.eye );
|
||||
const float cosLo = max(0.0, dot(surface.normal.eye, Lo));
|
||||
|
||||
for ( uint i = 0; i < ubo.lights; ++i ) {
|
||||
const Light light = lights[i];
|
||||
if ( light.power <= LIGHT_POWER_CUTOFF ) continue;
|
||||
const vec3 Lp = light.position;
|
||||
const vec3 Liu = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position, 1)) - surface.position.eye;
|
||||
const vec3 Li = normalize(Liu);
|
||||
#if VXGI_SHADOWS
|
||||
const float Ls = i < ubo.shadowSamples ? shadowFactor( light, 0.0 ) : 1.0;
|
||||
#else
|
||||
const float Ls = shadowFactor( light, 0.0 );
|
||||
#endif
|
||||
const float La = 1.0 / (PI * pow(length(Liu), 2.0));
|
||||
if ( light.power * La * Ls <= LIGHT_POWER_CUTOFF ) continue;
|
||||
|
||||
const float cosLi = max(0.0, dot(surface.normal.eye, Li));
|
||||
const vec3 Lr = light.color.rgb * light.power * La * Ls;
|
||||
#if LAMBERT
|
||||
const vec3 diffuse = surface.material.albedo.rgb;
|
||||
const vec3 specular = vec3(0);
|
||||
#elif PBR
|
||||
const vec3 Lh = normalize(Li + Lo);
|
||||
const float cosLh = max(0.0, dot(surface.normal.eye, Lh));
|
||||
|
||||
const vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) );
|
||||
const float D = ndfGGX( cosLh, surface.material.roughness );
|
||||
const float G = gaSchlickGGX(cosLi, cosLo, surface.material.roughness);
|
||||
const vec3 diffuse = mix( vec3(1.0) - F, vec3(0.0), surface.material.metallic ) * surface.material.albedo.rgb;
|
||||
const vec3 specular = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
|
||||
#endif
|
||||
// lightmapped, compute only specular
|
||||
if ( light.type >= 0 && 0 <= material.indexLightmap ) surface.fragment.rgb += (specular) * Lr * cosLi;
|
||||
// point light, compute only diffuse
|
||||
// else if ( abs(light.type) == 1 ) surface.fragment.rgb += (diffuse) * Lr * cosLi;
|
||||
else surface.fragment.rgb += (diffuse + specular) * Lr * cosLi;
|
||||
// surface.fragment.a += light.power * La * Ls;
|
||||
}
|
||||
}
|
||||
|
||||
outFragColor = postProcess();
|
||||
pbr();
|
||||
postProcess();
|
||||
}
|
||||
17
bin/data/shaders/display/subpass.vxgi.no-msaa.frag.glsl
Normal file
17
bin/data/shaders/display/subpass.vxgi.no-msaa.frag.glsl
Normal file
@ -0,0 +1,17 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define VXGI 1
|
||||
#define MULTISAMPLING 0
|
||||
#include "./subpass.h"
|
||||
|
||||
void main() {
|
||||
populateSurface();
|
||||
indirectLighting();
|
||||
|
||||
const vec3 ambient = ubo.ambient.rgb * surface.material.occlusion + surface.material.indirect.rgb;
|
||||
surface.fragment.rgb += (0 <= surface.material.indexLightmap) ? (surface.material.albedo.rgb + ambient) : (surface.material.albedo.rgb * ambient);
|
||||
|
||||
pbr();
|
||||
postProcess();
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
#version 450
|
||||
#pragma shader_stage(compute)
|
||||
|
||||
#extension GL_EXT_samplerless_texture_functions : require
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
|
||||
|
||||
#define MULTISAMPLING 1
|
||||
#define DEFERRED_SAMPLING 1
|
||||
#define DEFERRED_SAMPLING 0
|
||||
|
||||
#define LAMBERT 1
|
||||
#define PBR 0
|
||||
@ -176,7 +178,11 @@ layout (std140, binding = 8) readonly buffer DrawCalls {
|
||||
layout (binding = 9, rg16ui) uniform volatile coherent uimage3D voxelId[CASCADES];
|
||||
layout (binding = 10, rg16f) uniform volatile coherent image3D voxelUv[CASCADES];
|
||||
layout (binding = 11, rg16f) uniform volatile coherent image3D voxelNormal[CASCADES];
|
||||
layout (binding = 12, rgba8) uniform volatile coherent image3D voxelRadiance[CASCADES];
|
||||
#if VXGI_HDR
|
||||
layout (binding = 12, rgba8) uniform volatile coherent image3D voxelRadiance[CASCADES];
|
||||
#else
|
||||
layout (binding = 12, rgba16f) uniform volatile coherent image3D voxelRadiance[CASCADES];
|
||||
#endif
|
||||
|
||||
layout (binding = 13) uniform sampler3D samplerNoise;
|
||||
layout (binding = 14) uniform samplerCube samplerSkybox;
|
||||
@ -281,8 +287,9 @@ float shadowFactor( const Light light, float def ) {
|
||||
#endif
|
||||
}
|
||||
|
||||
float SCALE_CASCADE( uint x ) {
|
||||
float cascadePower( uint x ) {
|
||||
return pow(1 + x, ubo.cascadePower);
|
||||
// return max( 1, x * ubo.cascadePower );
|
||||
}
|
||||
|
||||
void main() {
|
||||
@ -291,7 +298,7 @@ void main() {
|
||||
surface.normal.world = decodeNormals( vec2(imageLoad(voxelNormal[CASCADE], ivec3(tUvw) ).xy) );
|
||||
surface.normal.eye = vec3( ubo.matrices.vxgi * vec4( surface.normal.world, 0.0f ) );
|
||||
|
||||
surface.position.eye = (vec3(gl_GlobalInvocationID.xyz) / vec3(imageSize(voxelRadiance[CASCADE])) * 2.0f - 1.0f) * SCALE_CASCADE(CASCADE);
|
||||
surface.position.eye = (vec3(gl_GlobalInvocationID.xyz) / vec3(imageSize(voxelRadiance[CASCADE])) * 2.0f - 1.0f) * cascadePower(CASCADE);
|
||||
surface.position.world = vec3( inverse(ubo.matrices.vxgi) * vec4( surface.position.eye, 1.0f ) );
|
||||
|
||||
const uvec2 ID = uvec2(imageLoad(voxelId[CASCADE], ivec3(tUvw) ).xy);
|
||||
|
||||
@ -1,40 +1,15 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define UF_DEFERRED_SAMPLING 0
|
||||
#define UF_CAN_DISCARD 1
|
||||
|
||||
#define PI 3.1415926536f
|
||||
|
||||
#define MAX_TEXTURES TEXTURES
|
||||
layout (constant_id = 0) const uint TEXTURES = 1;
|
||||
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
layout (binding = 0) uniform sampler2D samplerTextures[TEXTURES];
|
||||
|
||||
struct Material {
|
||||
vec4 colorBase;
|
||||
vec4 colorEmissive;
|
||||
|
||||
float factorMetallic;
|
||||
float factorRoughness;
|
||||
float factorOcclusion;
|
||||
float factorAlphaCutoff;
|
||||
|
||||
int indexAlbedo;
|
||||
int indexNormal;
|
||||
int indexEmissive;
|
||||
int indexOcclusion;
|
||||
|
||||
int indexMetallicRoughness;
|
||||
int modeAlpha;
|
||||
int padding1;
|
||||
int padding2;
|
||||
};
|
||||
struct Texture {
|
||||
int index;
|
||||
int samp;
|
||||
int remap;
|
||||
float blend;
|
||||
|
||||
vec4 lerp;
|
||||
};
|
||||
layout (std140, binding = 1) readonly buffer Materials {
|
||||
Material materials[];
|
||||
};
|
||||
@ -52,42 +27,23 @@ layout (location = 8) flat in ivec4 inId;
|
||||
|
||||
layout (location = 0) out uvec2 outId;
|
||||
layout (location = 1) out vec2 outNormals;
|
||||
#if UF_DEFERRED_SAMPLING
|
||||
#if DEFERRED_SAMPLING
|
||||
layout (location = 2) out vec2 outUvs;
|
||||
#else
|
||||
layout (location = 2) out vec4 outAlbedo;
|
||||
#endif
|
||||
|
||||
vec2 encodeNormals( vec3 n ) {
|
||||
// return n.xy / sqrt(n.z*8+8) + 0.5;
|
||||
return (vec2(atan(n.y,n.x)/PI, n.z)+1.0)*0.5;
|
||||
}
|
||||
|
||||
float wrap( float i ) {
|
||||
return fract(i);
|
||||
}
|
||||
vec2 wrap( vec2 uv ) {
|
||||
return vec2( wrap( uv.x ), wrap( uv.y ) );
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
const vec2 dx_vtc = dFdx(uv);
|
||||
const vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
bool validTextureIndex( int textureIndex ) {
|
||||
return 0 <= textureIndex && textureIndex < TEXTURES;
|
||||
}
|
||||
void main() {
|
||||
const float mip = mipLevel(inUv.xy);
|
||||
const vec2 uv = wrap(inUv.xy);
|
||||
vec4 A = vec4(0, 0, 0, 0);
|
||||
const vec3 P = inPosition;
|
||||
const vec3 N = inNormal;
|
||||
#if UF_DEFERRED_SAMPLING
|
||||
#if DEFERRED_SAMPLING
|
||||
outUvs = wrap(inUv.xy);
|
||||
const vec4 outAlbedo = vec4(0,0,0,0);
|
||||
#endif
|
||||
#if !UF_DEFERRED_SAMPLING
|
||||
#if !DEFERRED_SAMPLING
|
||||
A = textureLod( samplerTextures[0], inSt, mip );
|
||||
#endif
|
||||
outNormals = encodeNormals( N );
|
||||
|
||||
@ -1,117 +1,20 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
#define LAMBERT 1
|
||||
#define PBR 0
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
const float EPSILON = 0.00001;
|
||||
|
||||
const float LIGHT_POWER_CUTOFF = 0.005;
|
||||
|
||||
|
||||
const vec2 poissonDisk[16] = vec2[](
|
||||
vec2( -0.94201624, -0.39906216 ),
|
||||
vec2( 0.94558609, -0.76890725 ),
|
||||
vec2( -0.094184101, -0.92938870 ),
|
||||
vec2( 0.34495938, 0.29387760 ),
|
||||
vec2( -0.91588581, 0.45771432 ),
|
||||
vec2( -0.81544232, -0.87912464 ),
|
||||
vec2( -0.38277543, 0.27676845 ),
|
||||
vec2( 0.97484398, 0.75648379 ),
|
||||
vec2( 0.44323325, -0.97511554 ),
|
||||
vec2( 0.53742981, -0.47373420 ),
|
||||
vec2( -0.26496911, -0.41893023 ),
|
||||
vec2( 0.79197514, 0.19090188 ),
|
||||
vec2( -0.24188840, 0.99706507 ),
|
||||
vec2( -0.81409955, 0.91437590 ),
|
||||
vec2( 0.19984126, 0.78641367 ),
|
||||
vec2( 0.14383161, -0.14100790 )
|
||||
);
|
||||
|
||||
layout (constant_id = 0) const uint TEXTURES = 1;
|
||||
layout (binding = 0) uniform sampler2D samplerTextures[TEXTURES];
|
||||
|
||||
struct Material {
|
||||
vec4 colorBase;
|
||||
vec4 colorEmissive;
|
||||
#define SHADOW_SAMPLES 16
|
||||
|
||||
float factorMetallic;
|
||||
float factorRoughness;
|
||||
float factorOcclusion;
|
||||
float factorAlphaCutoff;
|
||||
#define PBR 0
|
||||
#define LAMBERT 1
|
||||
|
||||
int indexAlbedo;
|
||||
int indexNormal;
|
||||
int indexEmissive;
|
||||
int indexOcclusion;
|
||||
|
||||
int indexMetallicRoughness;
|
||||
int indexAtlas;
|
||||
int indexLightmap;
|
||||
int modeAlpha;
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
int index;
|
||||
int samp;
|
||||
int remap;
|
||||
float blend;
|
||||
|
||||
vec4 lerp;
|
||||
};
|
||||
|
||||
struct Light {
|
||||
vec3 position;
|
||||
float radius;
|
||||
|
||||
vec3 color;
|
||||
float power;
|
||||
|
||||
int type;
|
||||
int mapIndex;
|
||||
float depthBias;
|
||||
float padding;
|
||||
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
};
|
||||
|
||||
struct Ray {
|
||||
vec3 origin;
|
||||
vec3 direction;
|
||||
|
||||
vec3 position;
|
||||
float distance;
|
||||
};
|
||||
|
||||
struct Space {
|
||||
vec3 eye;
|
||||
vec3 world;
|
||||
};
|
||||
|
||||
struct SurfaceMaterial {
|
||||
uint id;
|
||||
|
||||
vec4 albedo;
|
||||
vec4 indirect;
|
||||
|
||||
float metallic;
|
||||
float roughness;
|
||||
float occlusion;
|
||||
};
|
||||
|
||||
struct Surface {
|
||||
vec2 uv;
|
||||
Space position;
|
||||
Space normal;
|
||||
|
||||
Ray ray;
|
||||
|
||||
SurfaceMaterial material;
|
||||
|
||||
vec4 fragment;
|
||||
} surface;
|
||||
#include "../../common/macros.h"
|
||||
#include "../../common/structs.h"
|
||||
#include "../../common/functions.h"
|
||||
#include "../../common/shadows.h"
|
||||
|
||||
layout (std140, binding = 3) readonly buffer Materials {
|
||||
Material materials[];
|
||||
@ -132,110 +35,6 @@ layout (location = 7) flat in ivec4 inId;
|
||||
|
||||
layout (location = 0) out vec4 outAlbedo;
|
||||
|
||||
// GGX/Towbridge-Reitz normal distribution function.
|
||||
// Uses Disney's reparametrization of alpha = roughness^2.
|
||||
float ndfGGX(float cosLh, float roughness) {
|
||||
const float alpha = roughness * roughness;
|
||||
const float alphaSq = alpha * alpha;
|
||||
const float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
|
||||
return alphaSq / (PI * denom * denom);
|
||||
}
|
||||
|
||||
// Single term for separable Schlick-GGX below.
|
||||
float gaSchlickG1(float cosTheta, float k) {
|
||||
return cosTheta / (cosTheta * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
|
||||
float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
|
||||
const float r = roughness + 1.0;
|
||||
const float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
|
||||
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
|
||||
}
|
||||
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
float random(vec3 seed, int i){
|
||||
return fract(sin(dot(vec4(seed,i), vec4(12.9898,78.233,45.164,94.673))) * 43758.5453);
|
||||
}
|
||||
|
||||
// Returns a vector that is orthogonal to u.
|
||||
vec3 orthogonal(vec3 u){
|
||||
u = normalize(u);
|
||||
const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
|
||||
return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
|
||||
}
|
||||
float rand2(vec2 co){
|
||||
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453);
|
||||
}
|
||||
float rand3(vec3 co){
|
||||
return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453);
|
||||
}
|
||||
|
||||
vec3 decodeNormals( vec2 enc ) {
|
||||
const vec2 ang = enc*2-1;
|
||||
const vec2 scth = vec2( sin(ang.x * PI), cos(ang.x * PI) );
|
||||
const vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y);
|
||||
return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) );
|
||||
/*
|
||||
vec2 fenc = enc*4-2;
|
||||
float f = dot(fenc,fenc);
|
||||
float g = sqrt(1-f/4);
|
||||
return normalize( vec3(fenc * g, 1 - f / 2) );
|
||||
*/
|
||||
}
|
||||
float wrap( float i ) {
|
||||
return fract(i);
|
||||
}
|
||||
vec2 wrap( vec2 uv ) {
|
||||
return vec2( wrap( uv.x ), wrap( uv.y ) );
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
const vec2 dx_vtc = dFdx(uv);
|
||||
const vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
bool validTextureIndex( int textureIndex ) {
|
||||
return 0 <= textureIndex; // && textureIndex < ubo.textures;
|
||||
}
|
||||
|
||||
float shadowFactor( const Light light, float def ) {
|
||||
if ( !validTextureIndex(light.mapIndex) ) return 1.0;
|
||||
|
||||
vec4 positionClip = light.projection * light.view * vec4(surface.position.world, 1.0);
|
||||
positionClip.xyz /= positionClip.w;
|
||||
|
||||
if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0;
|
||||
if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0;
|
||||
if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0;
|
||||
|
||||
float factor = 1.0;
|
||||
|
||||
// spot light
|
||||
if ( abs(light.type) == 2 || abs(light.type) == 3 ) {
|
||||
const float dist = length( positionClip.xy );
|
||||
if ( dist > 0.5 ) return def; //0.0;
|
||||
|
||||
// spot light with attenuation
|
||||
if ( abs(light.type) == 3 ) {
|
||||
factor = 1.0 - (pow(dist * 2,2.0));
|
||||
}
|
||||
}
|
||||
|
||||
const vec2 uv = positionClip.xy * 0.5 + 0.5;
|
||||
const float bias = light.depthBias;
|
||||
const float eyeDepth = positionClip.z;
|
||||
const int samples = 16; //int(ubo.poissonSamples);
|
||||
if ( samples <= 1 ) return eyeDepth < texture(samplerTextures[nonuniformEXT(light.mapIndex)], uv).r - bias ? 0.0 : factor;
|
||||
for ( int i = 0; i < samples; ++i ) {
|
||||
const int index = int( float(samples) * random(floor(surface.position.world * 1000.0), i)) % samples;
|
||||
const float lightDepth = texture(samplerTextures[nonuniformEXT(light.mapIndex)], uv + poissonDisk[index] / 700.0 ).r;
|
||||
if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples;
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 A = vec4(1, 1, 1, 1);
|
||||
surface.normal.world = normalize( inNormal );
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
|
||||
@ -1,40 +1,16 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
#define DEFERRED_SAMPLING 1
|
||||
#define CAN_DISCARD 1
|
||||
#define USE_LIGHTMAP 1
|
||||
|
||||
#define MAX_TEXTURES textures.length()
|
||||
layout (constant_id = 0) const uint TEXTURES = 1;
|
||||
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
|
||||
layout (binding = 0) uniform sampler2D samplerTextures[TEXTURES];
|
||||
|
||||
struct Material {
|
||||
vec4 colorBase;
|
||||
vec4 colorEmissive;
|
||||
|
||||
float factorMetallic;
|
||||
float factorRoughness;
|
||||
float factorOcclusion;
|
||||
float factorAlphaCutoff;
|
||||
|
||||
int indexAlbedo;
|
||||
int indexNormal;
|
||||
int indexEmissive;
|
||||
int indexOcclusion;
|
||||
|
||||
int indexMetallicRoughness;
|
||||
int indexAtlas;
|
||||
int indexLightmap;
|
||||
int modeAlpha;
|
||||
};
|
||||
struct Texture {
|
||||
int index;
|
||||
int samp;
|
||||
int remap;
|
||||
float blend;
|
||||
|
||||
vec4 lerp;
|
||||
};
|
||||
layout (std140, binding = 1) readonly buffer Materials {
|
||||
Material materials[];
|
||||
};
|
||||
@ -42,6 +18,8 @@ layout (std140, binding = 2) readonly buffer Textures {
|
||||
Texture textures[];
|
||||
};
|
||||
|
||||
#include "../common/functions.h"
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in vec2 inSt;
|
||||
layout (location = 2) in vec4 inColor;
|
||||
@ -58,26 +36,6 @@ layout (location = 1) out vec2 outNormals;
|
||||
layout (location = 2) out vec4 outAlbedo;
|
||||
#endif
|
||||
|
||||
#define PI 3.1415926536f
|
||||
|
||||
vec2 encodeNormals( vec3 n ) {
|
||||
// return n.xy / sqrt(n.z*8+8) + 0.5;
|
||||
return (vec2(atan(n.y,n.x)/PI, n.z)+1.0)*0.5;
|
||||
}
|
||||
float wrap( float i ) {
|
||||
return fract(i);
|
||||
}
|
||||
vec2 wrap( vec2 uv ) {
|
||||
return vec2( wrap( uv.x ), wrap( uv.y ) );
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
const vec2 dx_vtc = dFdx(uv);
|
||||
const vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
bool validTextureIndex( int textureIndex ) {
|
||||
return 0 <= textureIndex && textureIndex < textures.length();
|
||||
}
|
||||
void main() {
|
||||
const float mip = mipLevel(inUv.xy);
|
||||
const vec2 uv = wrap(inUv.xy);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
|
||||
@ -1,41 +1,17 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
#define DEFERRED_SAMPLING 1
|
||||
#define USE_LIGHTMAP 1
|
||||
|
||||
#define PI 3.1415926536f
|
||||
#define BLEND 1
|
||||
#define DEPTH_TEST 1
|
||||
|
||||
layout (constant_id = 0) const uint CASCADES = 16;
|
||||
layout (constant_id = 1) const uint TEXTURES = 512;
|
||||
|
||||
struct Material {
|
||||
vec4 colorBase;
|
||||
vec4 colorEmissive;
|
||||
#define MAX_TEXTURES textures.length()
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
|
||||
float factorMetallic;
|
||||
float factorRoughness;
|
||||
float factorOcclusion;
|
||||
float factorAlphaCutoff;
|
||||
|
||||
int indexAlbedo;
|
||||
int indexNormal;
|
||||
int indexEmissive;
|
||||
int indexOcclusion;
|
||||
|
||||
int indexMetallicRoughness;
|
||||
int indexAtlas;
|
||||
int indexLightmap;
|
||||
int modeAlpha;
|
||||
};
|
||||
struct Texture {
|
||||
int index;
|
||||
int samp;
|
||||
int remap;
|
||||
float blend;
|
||||
|
||||
vec4 lerp;
|
||||
};
|
||||
layout (std140, binding = 0) readonly buffer Materials {
|
||||
Material materials[];
|
||||
};
|
||||
@ -43,6 +19,7 @@ layout (std140, binding = 1) readonly buffer Textures {
|
||||
Texture textures[];
|
||||
};
|
||||
|
||||
#include "../common/functions.h"
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in vec2 inSt;
|
||||
@ -57,7 +34,12 @@ layout (binding = 7) uniform sampler2D samplerTextures[TEXTURES];
|
||||
layout (binding = 8, rg16ui) uniform volatile coherent uimage3D voxelId[CASCADES];
|
||||
layout (binding = 9, rg16f) uniform volatile coherent image3D voxelUv[CASCADES];
|
||||
layout (binding = 10, rg16f) uniform volatile coherent image3D voxelNormal[CASCADES];
|
||||
layout (binding = 11, rgba8) uniform volatile coherent image3D voxelRadiance[CASCADES];
|
||||
#if VXGI_HDR
|
||||
layout (binding = 11, rgba16f) uniform volatile coherent image3D voxelRadiance[CASCADES];
|
||||
#else
|
||||
layout (binding = 11, rgba8) uniform volatile coherent image3D voxelRadiance[CASCADES];
|
||||
#endif
|
||||
// layout (binding = 12, r16f) uniform volatile coherent image3D voxelDepth[CASCADES];
|
||||
|
||||
layout (location = 0) out uvec2 outId;
|
||||
layout (location = 1) out vec2 outNormals;
|
||||
@ -67,25 +49,6 @@ layout (location = 1) out vec2 outNormals;
|
||||
layout (location = 2) out vec4 outAlbedo;
|
||||
#endif
|
||||
|
||||
vec2 encodeNormals( vec3 n ) {
|
||||
// return n.xy / sqrt(n.z*8+8) + 0.5;
|
||||
return (vec2(atan(n.y,n.x)/PI, n.z)+1.0)*0.5;
|
||||
}
|
||||
float wrap( float i ) {
|
||||
return fract(i);
|
||||
}
|
||||
vec2 wrap( vec2 uv ) {
|
||||
return vec2( wrap( uv.x ), wrap( uv.y ) );
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
const vec2 dx_vtc = dFdx(uv);
|
||||
const vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
bool validTextureIndex( int textureIndex ) {
|
||||
return 0 <= textureIndex && textureIndex < textures.length();
|
||||
}
|
||||
|
||||
void main() {
|
||||
const uint CASCADE = inId.z;
|
||||
if ( CASCADES <= CASCADE ) discard;
|
||||
@ -95,7 +58,7 @@ void main() {
|
||||
vec4 A = vec4(0, 0, 0, 0);
|
||||
const vec3 N = inNormal;
|
||||
const vec2 uv = wrap(inUv.xy);
|
||||
const float mip = mipLevel(inUv.xy);
|
||||
const float mip = 0; // mipLevel(inUv.xy);
|
||||
const int materialId = int(inId.y);
|
||||
const Material material = materials[materialId];
|
||||
|
||||
@ -136,13 +99,35 @@ void main() {
|
||||
}
|
||||
#endif
|
||||
|
||||
const vec4 outAlbedo = A * inColor;
|
||||
const float outDepth = length(inPosition.xzy);
|
||||
const vec4 inAlbedo = imageLoad(voxelRadiance[CASCADE], ivec3(P * imageSize(voxelRadiance[CASCADE])));
|
||||
#if DEPTH_TEST
|
||||
{
|
||||
// const float depth = imageLoad(voxelDepth[CASCADE], ivec3(P * imageSize(voxelDepth[CASCADE]))).r;
|
||||
const float inDepth = inAlbedo.a;
|
||||
if ( inDepth != 0 && inDepth < outDepth ) discard;
|
||||
// imageStore(voxelDepth[CASCADE], ivec3(P * imageSize(voxelUv[CASCADE])), vec4(inDepth, 0, 0, 0));
|
||||
}
|
||||
#endif
|
||||
|
||||
// const vec4 outAlbedo = A * inColor;
|
||||
const vec4 outAlbedo = vec4( A.rgb * inColor.rgb, outDepth );
|
||||
const uvec2 outId = uvec2(inId.w+1, inId.y+1);
|
||||
const vec2 outNormals = encodeNormals( normalize( N ) );
|
||||
const vec2 outUvs = wrap(inUv.xy);
|
||||
|
||||
imageStore(voxelId[CASCADE], ivec3(P * imageSize(voxelId[CASCADE])), uvec4(outId, 0, 0));
|
||||
imageStore(voxelNormal[CASCADE], ivec3(P * imageSize(voxelNormal[CASCADE])), vec4(outNormals, 0, 0));
|
||||
imageStore(voxelUv[CASCADE], ivec3(P * imageSize(voxelUv[CASCADE])), vec4(outUvs, 0, 0));
|
||||
#if BLEND
|
||||
{
|
||||
const ivec3 uvw = ivec3(P * imageSize(voxelNormal[CASCADE]));
|
||||
const vec3 inNormal = decodeNormals( imageLoad(voxelNormal[CASCADE], uvw).xy );
|
||||
const vec4 store = vec4( encodeNormals(normalize(inNormal + N)), 0, 0 );
|
||||
imageStore(voxelNormal[CASCADE], uvw, store);
|
||||
|
||||
imageStore(voxelRadiance[CASCADE], ivec3(P * imageSize(voxelRadiance[CASCADE])), inAlbedo.a > 0 ? (inAlbedo + outAlbedo) * 0.5 : outAlbedo);
|
||||
}
|
||||
#else
|
||||
imageStore(voxelNormal[CASCADE], ivec3(P * imageSize(voxelNormal[CASCADE])), vec4(outNormals, 0, 0));
|
||||
imageStore(voxelRadiance[CASCADE], ivec3(P * imageSize(voxelRadiance[CASCADE])), outAlbedo);
|
||||
#endif
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(geometry)
|
||||
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 3) out;
|
||||
@ -27,8 +28,9 @@ layout (binding = 6) uniform UBO {
|
||||
float padding3;
|
||||
} ubo;
|
||||
|
||||
float SCALE_CASCADE( uint x ) {
|
||||
float cascadePower( uint x ) {
|
||||
return pow(1 + x, ubo.cascadePower);
|
||||
// return max( 1, x * ubo.cascadePower );
|
||||
}
|
||||
|
||||
void main(){
|
||||
@ -47,12 +49,11 @@ void main(){
|
||||
|
||||
const uint CASCADE = inId[0].z;
|
||||
vec3 P[3] = {
|
||||
vec3( ubo.voxel * vec4( inPosition[0], 1 ) ) / SCALE_CASCADE(CASCADE),
|
||||
vec3( ubo.voxel * vec4( inPosition[1], 1 ) ) / SCALE_CASCADE(CASCADE),
|
||||
vec3( ubo.voxel * vec4( inPosition[2], 1 ) ) / SCALE_CASCADE(CASCADE),
|
||||
vec3( ubo.voxel * vec4( inPosition[0], 1 ) ) / cascadePower(CASCADE),
|
||||
vec3( ubo.voxel * vec4( inPosition[1], 1 ) ) / cascadePower(CASCADE),
|
||||
vec3( ubo.voxel * vec4( inPosition[2], 1 ) ) / cascadePower(CASCADE),
|
||||
};
|
||||
|
||||
|
||||
for( uint i = 0; i < 3; ++i ){
|
||||
const vec3 D = normalize( inPosition[i] - C ) * HALF_PIXEL;
|
||||
|
||||
@ -61,7 +62,7 @@ void main(){
|
||||
outColor = inColor[i];
|
||||
outNormal = inNormal[i];
|
||||
outTBN = inTBN[i];
|
||||
outPosition = P[i]; // + D;
|
||||
outPosition = P[i] + D;
|
||||
outId = inId[i];
|
||||
|
||||
const vec3 P = outPosition; // + D;
|
||||
@ -70,9 +71,9 @@ void main(){
|
||||
else if ( N.x > N.y && N.x > N.z ) gl_Position = vec4(P.y, P.z, 0, 1);
|
||||
else gl_Position = vec4(P.x, P.z, 0, 1);
|
||||
#else
|
||||
if ( A == 0 ) gl_Position = vec4(P.zy, 1, 1 );
|
||||
else if ( A == 1 ) gl_Position = vec4(P.xz, 1, 1 );
|
||||
else if ( A == 2 ) gl_Position = vec4(P.xy, 1, 1 );
|
||||
if ( A == 0 ) gl_Position = vec4(P.zy, 0, 1 );
|
||||
else if ( A == 1 ) gl_Position = vec4(P.xz, 0, 1 );
|
||||
else if ( A == 2 ) gl_Position = vec4(P.xy, 0, 1 );
|
||||
#endif
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define TEXTURES 1
|
||||
#define DEFERRED_SAMPLING 0
|
||||
#define CAN_DISCARD 1
|
||||
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
layout (binding = 1) uniform sampler2D samplerTexture;
|
||||
|
||||
@ -24,22 +29,6 @@ layout (location = 1) out vec2 outNormals;
|
||||
layout (location = 2) out vec4 outAlbedo;
|
||||
#endif
|
||||
|
||||
float wrap( float i ) {
|
||||
return fract(i);
|
||||
}
|
||||
vec2 wrap( vec2 uv ) {
|
||||
return vec2( wrap( uv.x ), wrap( uv.y ) );
|
||||
}
|
||||
vec2 encodeNormals( vec3 n ) {
|
||||
float p = sqrt(n.z*8+8);
|
||||
return n.xy/p + 0.5;
|
||||
}
|
||||
float mipLevel( in vec2 uv ) {
|
||||
vec2 dx_vtc = dFdx(uv);
|
||||
vec2 dy_vtc = dFdy(uv);
|
||||
return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
if ( inUv.x < inGui.offset.x ) discard;
|
||||
if ( inUv.y < inGui.offset.y ) discard;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec2 inPos;
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
#define TEXTURES 1
|
||||
#define DEFERRED_SAMPLING 0
|
||||
#define CAN_DISCARD 1
|
||||
|
||||
#include "../common/macros.h"
|
||||
#include "../common/structs.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
layout (binding = 1) uniform sampler2D samplerTexture;
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec2 inPos;
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout (binding = 1) uniform sampler2D samplerColor;
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in vec3 inNormal;
|
||||
layout (location = 2) in vec4 inColor;
|
||||
|
||||
layout (location = 0) out vec4 outAlbedoSpecular;
|
||||
layout (location = 1) out vec4 outNormal;
|
||||
|
||||
void main() {
|
||||
outAlbedoSpecular = texture(samplerColor, inUv);
|
||||
outAlbedoSpecular = vec4(1,1,1,1);
|
||||
outAlbedoSpecular.rgb *= inColor.rgb;
|
||||
outAlbedoSpecular.a = 1;
|
||||
// outPosition = vec4(inPosition, 1.0);
|
||||
outNormal = vec4(normalize(inNormal), 1.0);
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUv;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in uint inColor;
|
||||
|
||||
layout( push_constant ) uniform PushBlock {
|
||||
uint pass;
|
||||
uint draw;
|
||||
} PushConstant;
|
||||
|
||||
struct Matrices {
|
||||
mat4 model;
|
||||
mat4 view[PASSES];
|
||||
mat4 projection[PASSES];
|
||||
};
|
||||
|
||||
layout (binding = 0) uniform UBO {
|
||||
Matrices matrices;
|
||||
vec4 color;
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec2 outUv;
|
||||
layout (location = 1) out vec3 outNormal;
|
||||
layout (location = 2) out vec4 outColor;
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
|
||||
void main() {
|
||||
outUv = inUv;
|
||||
// outColor = ubo.color;
|
||||
|
||||
// outPosition = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inPos.xyz, 1.0));
|
||||
outNormal = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inNormal.xyz, 0.0));
|
||||
|
||||
// outPosition = vec3(ubo.matrices.model * vec4(inPos.xyz, 1.0));
|
||||
// outNormal = vec3(ubo.matrices.model * vec4(inNormal.xyz, 0.0));
|
||||
|
||||
outColor.a = (inColor >> 24u) & 0xFF;
|
||||
outColor.b = (inColor >> 16u) & 0xFF;
|
||||
outColor.g = (inColor >> 8u) & 0xFF;
|
||||
outColor.r = (inColor ) & 0xFF;
|
||||
outColor.rgb /= 256.0;
|
||||
outColor.a = 0.5;
|
||||
|
||||
gl_Position = ubo.matrices.projection[PushConstant.pass] * ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inPos.xyz, 1.0);
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
layout (location = 0) in vec3 inColor;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
@ -1,23 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout (binding = 1) uniform sampler3D samplerColor;
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in vec4 inColor;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in vec3 inPosition;
|
||||
|
||||
layout (location = 0) out vec4 outAlbedoSpecular;
|
||||
layout (location = 1) out vec4 outNormal;
|
||||
layout (location = 2) out vec4 outPosition;
|
||||
|
||||
layout (binding = 2) uniform UBO {
|
||||
float slice;
|
||||
} ubo;
|
||||
|
||||
void main() {
|
||||
outAlbedoSpecular.rgb = vec3(texture(samplerColor, vec3(inUv,ubo.slice)).r);
|
||||
outAlbedoSpecular.a = 1;
|
||||
outNormal = vec4(inNormal,1);
|
||||
outPosition = vec4(inPosition,1);
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(compute)
|
||||
|
||||
layout (local_size_x = 32, local_size_y = 32) in;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
layout (binding = 1) uniform sampler2D samplerColor;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 6;
|
||||
layout (location = 0) in vec3 inPos;
|
||||
@ -1,11 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout (binding = 0) uniform sampler2D samplerColor;
|
||||
layout (location = 0) in vec2 inUV;
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main() {
|
||||
vec4 sampledColor = texture(samplerColor, inUV);
|
||||
if ( sampledColor.r <= 0.000001 && sampledColor.g <= 0.000001 && sampledColor.b <= 0.000001 ) discard;
|
||||
outFragColor = sampledColor;
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) out vec2 outUV;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
||||
gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
|
||||
}
|
||||
@ -19,34 +19,17 @@ void client::initialize() {
|
||||
uf::renderer::device.window = &client::window;
|
||||
|
||||
ext::load();
|
||||
#if 1
|
||||
|
||||
client::config = ext::config;
|
||||
#else
|
||||
/* Initialize config */ {
|
||||
struct {
|
||||
uf::Serializer ext;
|
||||
uf::Serializer fallback;
|
||||
} config;
|
||||
/* Get configuration */ {
|
||||
config.ext = ext::config.serialize();
|
||||
}
|
||||
/* Merge */ {
|
||||
client::config = config.ext;
|
||||
client::config.merge( config.fallback, true );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize window */ {
|
||||
// Window size
|
||||
pod::Vector2i size; {
|
||||
size.x = client::config["window"]["size"]["x"].as<size_t>();
|
||||
size.y = client::config["window"]["size"]["y"].as<size_t>();
|
||||
// request system size
|
||||
if ( size.x <= 0 && size.y <= 0 ) {
|
||||
auto resolution = client::window.getResolution();
|
||||
client::config["window"]["size"]["x"] = (size.x = resolution.x);
|
||||
client::config["window"]["size"]["y"] = (size.y = resolution.y);
|
||||
}
|
||||
pod::Vector2i size = uf::vector::decode( client::config["window"]["size"], pod::Vector2i{} );
|
||||
// request system size
|
||||
if ( size.x <= 0 && size.y <= 0 ) {
|
||||
auto resolution = client::window.getResolution();
|
||||
client::config["window"]["size"][0] = (size.x = resolution.x);
|
||||
client::config["window"]["size"][1] = (size.y = resolution.y);
|
||||
}
|
||||
// Window title
|
||||
uf::String title; {
|
||||
@ -71,26 +54,14 @@ void client::initialize() {
|
||||
// client::window.setPosition({0, 0});
|
||||
// client::window.setMouseGrabbed(true);
|
||||
|
||||
/* Set Icon */ if ( client::config["window"]["icon"].is<std::string>() ) {
|
||||
if ( client::config["window"]["icon"].is<std::string>() ) {
|
||||
uf::Image icon;
|
||||
icon.open(client::config["window"]["icon"].as<std::string>());
|
||||
client::window.setIcon({(int) icon.getDimensions().x, (int) icon.getDimensions().y}, ((uint8_t*)icon.getPixelsPtr()));
|
||||
}
|
||||
client::window.setTitle(title); {
|
||||
uf::Serializer json;
|
||||
std::string hook = "window:Title.Changed";
|
||||
json["type"] = hook;
|
||||
json["invoker"] = "os";
|
||||
json["window"]["title"] = std::string(title);
|
||||
uf::hooks.call( hook, json );
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
uf::hooks.shouldPreferReadable();
|
||||
if ( client::config["engine"]["hook"]["mode"] == "Readable" ) {
|
||||
|
||||
}
|
||||
*/
|
||||
client::window.setTitle(title);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UF_USE_OPENAL
|
||||
@ -104,105 +75,53 @@ void client::initialize() {
|
||||
#endif
|
||||
|
||||
/* Initialize hooks */ {
|
||||
// if ( client::config["engine"]["hook"]["mode"] == "Both" || client::config["engine"]["hook"]["mode"] == "Readable" ) {
|
||||
uf::hooks.addHook( "window:Mouse.CursorVisibility", [&](const ext::json::Value& json){
|
||||
client::window.setCursorVisible(json["state"].as<bool>());
|
||||
client::window.setMouseGrabbed(!json["state"].as<bool>());
|
||||
client::config["mouse"]["visible"] = json["state"].as<bool>();
|
||||
client::config["window"]["mouse"]["center"] = !json["state"].as<bool>();
|
||||
});
|
||||
uf::hooks.addHook( "window:Mouse.Lock", [&](const ext::json::Value& json){
|
||||
if ( client::window.hasFocus() ) {
|
||||
client::window.setMousePosition(client::window.getSize()/2);
|
||||
}
|
||||
});
|
||||
uf::hooks.addHook( "window:Closed", [&](const ext::json::Value& json){
|
||||
client::ready = false;
|
||||
// std::exit(EXIT_SUCCESS);
|
||||
} );
|
||||
uf::hooks.addHook( "window:Title.Changed", [&](const ext::json::Value& json){
|
||||
if ( json["invoker"] != "os" ) {
|
||||
if ( !ext::json::isObject( json["window"] ) ) return;
|
||||
uf::String title = json["window"]["title"].as<std::string>();
|
||||
client::window.setTitle(title);
|
||||
}
|
||||
} );
|
||||
uf::hooks.addHook( "window:Resized", [&](const ext::json::Value& json){
|
||||
pod::Vector2i size; {
|
||||
size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
}
|
||||
if ( json["invoker"] != "os" ) {
|
||||
client::window.setSize(size);
|
||||
}
|
||||
// Update viewport
|
||||
if ( !ext::json::isArray( client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"] ) ) {
|
||||
float scale = client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].is<double>() ? client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as<float>() : 1;
|
||||
uf::renderer::settings::width = size.x * scale;
|
||||
uf::renderer::settings::height = size.y * scale;
|
||||
}
|
||||
uf::hooks.addHook( "window:Mouse.CursorVisibility", [&](const ext::json::Value& json){
|
||||
client::window.setCursorVisible(json["state"].as<bool>());
|
||||
client::window.setMouseGrabbed(!json["state"].as<bool>());
|
||||
client::config["mouse"]["visible"] = json["state"].as<bool>();
|
||||
client::config["window"]["mouse"]["center"] = !json["state"].as<bool>();
|
||||
});
|
||||
uf::hooks.addHook( "window:Mouse.Lock", [&](const ext::json::Value& json){
|
||||
if ( client::window.hasFocus() ) {
|
||||
client::window.setMousePosition(client::window.getSize()/2);
|
||||
}
|
||||
});
|
||||
uf::hooks.addHook( "window:Closed", [&](const ext::json::Value& json){
|
||||
client::ready = false;
|
||||
} );
|
||||
uf::hooks.addHook( "window:Title.Changed", [&](const ext::json::Value& json){
|
||||
if ( json["invoker"] != "os" ) {
|
||||
if ( !ext::json::isObject( json["window"] ) ) return;
|
||||
uf::String title = json["window"]["title"].as<std::string>();
|
||||
client::window.setTitle(title);
|
||||
}
|
||||
} );
|
||||
uf::hooks.addHook( "window:Resized", [&](const ext::json::Value& json){
|
||||
pod::Vector2i size = uf::vector::decode( json["window"]["size"], pod::Vector2i{} );
|
||||
if ( json["invoker"] != "os" ) client::window.setSize(size);
|
||||
// Update viewport
|
||||
if ( !ext::json::isArray( client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"] ) ) {
|
||||
float scale = client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].is<double>() ? client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as<float>() : 1;
|
||||
uf::renderer::settings::width = size.x * scale;
|
||||
uf::renderer::settings::height = size.y * scale;
|
||||
}
|
||||
|
||||
uf::renderer::states::resized = true;
|
||||
} );
|
||||
/*
|
||||
}
|
||||
else if ( client::config["engine"]["hook"]["mode"] == "Both" || client::config["engine"]["hook"]["mode"] == "Optimal" ) {
|
||||
uf::hooks.addHook( "window:Closed", [&](const uf::OptimalHook::argument_t& userdata)->uf::OptimalHook::return_t{
|
||||
client::ready = false;
|
||||
std::exit(EXIT_SUCCESS);
|
||||
return NULL;
|
||||
} );
|
||||
uf::hooks.addHook( "window:Title.Changed", [&](const uf::OptimalHook::argument_t& userdata)->uf::OptimalHook::return_t{
|
||||
// Hook information
|
||||
struct Hook {
|
||||
std::string type;
|
||||
std::string invoker;
|
||||
|
||||
struct {
|
||||
std::string title;
|
||||
} window;
|
||||
};
|
||||
const Hook& hook = uf::userdata::get<Hook>(userdata);
|
||||
if ( hook.invoker != "os" ) {
|
||||
uf::String title = hook.window.title;
|
||||
client::window.setTitle(title);
|
||||
}
|
||||
return NULL;
|
||||
} );
|
||||
uf::hooks.addHook( "window:Resized", [&](const uf::OptimalHook::argument_t& userdata)->uf::OptimalHook::return_t{
|
||||
// Hook information
|
||||
struct Hook {
|
||||
std::string type;
|
||||
std::string invoker;
|
||||
|
||||
struct {
|
||||
pod::Vector2i size;
|
||||
} window;
|
||||
};
|
||||
const Hook& hook = uf::userdata::get<Hook>(userdata);
|
||||
if ( hook.invoker != "os" ) {
|
||||
client::window.setSize(hook.window.size);
|
||||
}
|
||||
// Update viewport
|
||||
return NULL;
|
||||
} );
|
||||
}
|
||||
*/
|
||||
uf::renderer::states::resized = true;
|
||||
} );
|
||||
}
|
||||
#if !UF_ENV_DREAMCAST
|
||||
if ( client::config["window"]["mode"].as<std::string>() == "fullscreen" ) client::window.switchToFullscreen();
|
||||
if ( client::config["window"]["mode"].as<std::string>() == "borderless" ) client::window.switchToFullscreen( true );
|
||||
#endif
|
||||
|
||||
client::ready = true;
|
||||
#if UF_ENV_DREAMCAST
|
||||
client::window.pollEvents();
|
||||
#endif
|
||||
}
|
||||
void client::tick() {
|
||||
// uf::hooks.call("system:Tick");
|
||||
client::window.pollEvents();
|
||||
// call mouse move
|
||||
// query lock
|
||||
|
||||
if ( client::window.hasFocus() && client::config["window"]["mouse"]["center"].as<bool>() ) {
|
||||
auto previous = client::window.getMousePosition();
|
||||
client::window.setMousePosition(client::window.getSize()/2);
|
||||
@ -211,41 +130,13 @@ void client::tick() {
|
||||
auto size = client::window.getSize();
|
||||
uf::Serializer payload;
|
||||
payload["invoker"] = "client";
|
||||
payload["mouse"]["delta"]["x"] = previous.x - current.x;
|
||||
payload["mouse"]["delta"]["y"] = previous.y - current.y;
|
||||
payload["mouse"]["position"]["x"] = current.x;
|
||||
payload["mouse"]["position"]["y"] = current.y;
|
||||
payload["mouse"]["size"]["x"] = size.x;
|
||||
payload["mouse"]["size"]["y"] = size.y;
|
||||
payload["mouse"]["delta"] = uf::vector::encode( previous - current );
|
||||
payload["mouse"]["position"] = uf::vector::encode( current );
|
||||
payload["mouse"]["size"] = uf::vector::encode( size );
|
||||
payload["mouse"]["state"] = "???";
|
||||
payload["type"] = "window:Mouse.Moved";
|
||||
uf::hooks.call("window:Mouse.Moved", payload);
|
||||
}
|
||||
/*
|
||||
{
|
||||
"invoker" : "os",
|
||||
"mouse" :
|
||||
{
|
||||
"delta" :
|
||||
{
|
||||
"x" : -17,
|
||||
"y" : -12
|
||||
},
|
||||
"position" :
|
||||
{
|
||||
"x" : 371,
|
||||
"y" : 276
|
||||
},
|
||||
"size" :
|
||||
{
|
||||
"x" : 800,
|
||||
"y" : 600
|
||||
},
|
||||
"state" : "???"
|
||||
},
|
||||
"type" : "window:Mouse.Moved"
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void client::render() {
|
||||
|
||||
@ -47,8 +47,9 @@ int main(int argc, char** argv){
|
||||
json["type"] = hook;
|
||||
json["invoker"] = "ext";
|
||||
|
||||
json["window"]["size"]["x"] = client::config["window"]["size"]["x"];
|
||||
json["window"]["size"]["y"] = client::config["window"]["size"]["y"];
|
||||
json["window"]["size"] = client::config["window"]["size"];
|
||||
// json["window"]["size"]["x"] = client::config["window"]["size"]["x"];
|
||||
// json["window"]["size"]["y"] = client::config["window"]["size"]["y"];
|
||||
uf::hooks.call(hook, json);
|
||||
}
|
||||
#if UF_ENV_DREAMCAST
|
||||
|
||||
@ -14,6 +14,7 @@ namespace pod {
|
||||
std::vector<uf::renderer::Texture3D> uv;
|
||||
std::vector<uf::renderer::Texture3D> normal;
|
||||
std::vector<uf::renderer::Texture3D> radiance;
|
||||
std::vector<uf::renderer::Texture3D> depth;
|
||||
|
||||
pod::Matrix4f matrix;
|
||||
float cascadePower = 2.0;
|
||||
|
||||
@ -56,6 +56,8 @@ namespace ext {
|
||||
~Buffer();
|
||||
void initialize( VkDevice device );
|
||||
void destroy();
|
||||
|
||||
void aliasBuffer( const Buffer& );
|
||||
};
|
||||
struct UF_API Buffers {
|
||||
std::vector<Buffer> buffers;
|
||||
|
||||
@ -4,60 +4,13 @@
|
||||
#include <uf/ext/vulkan/swapchain.h>
|
||||
#include <uf/ext/vulkan/initializers.h>
|
||||
#include <uf/ext/vulkan/texture.h>
|
||||
#include <uf/ext/vulkan/shader.h>
|
||||
#include <uf/utils/graphic/descriptor.h>
|
||||
#include <uf/utils/graphic/mesh.h>
|
||||
|
||||
#define UF_GRAPHIC_POINTERED_USERDATA 1
|
||||
|
||||
namespace ext {
|
||||
namespace vulkan {
|
||||
#if UF_GRAPHIC_POINTERED_USERDATA
|
||||
typedef uf::PointeredUserdata userdata_t;
|
||||
#else
|
||||
typedef uf::Userdata userdata_t;
|
||||
#endif
|
||||
|
||||
ext::json::Value definitionToJson(/*const*/ ext::json::Value& definition );
|
||||
ext::vulkan::userdata_t jsonToUserdata( const ext::json::Value& payload, const ext::json::Value& definition );
|
||||
|
||||
struct Graphic;
|
||||
struct UF_API Shader : public Buffers {
|
||||
bool aliased = false;
|
||||
|
||||
Device* device = NULL;
|
||||
std::string filename = "";
|
||||
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
VkPipelineShaderStageCreateInfo descriptor;
|
||||
std::vector<VkDescriptorSetLayoutBinding> descriptorSetLayoutBindings;
|
||||
std::vector<VkSpecializationMapEntry> specializationMapEntries;
|
||||
VkSpecializationInfo specializationInfo;
|
||||
|
||||
uf::Serializer metadata;
|
||||
|
||||
ext::vulkan::userdata_t specializationConstants;
|
||||
std::vector<ext::vulkan::userdata_t> pushConstants;
|
||||
std::vector<ext::vulkan::userdata_t> uniforms;
|
||||
// ~Shader();
|
||||
void initialize( Device& device, const std::string&, VkShaderStageFlagBits );
|
||||
void destroy();
|
||||
bool validate();
|
||||
|
||||
bool hasUniform( const std::string& name );
|
||||
|
||||
Buffer* getUniformBuffer( const std::string& name );
|
||||
ext::vulkan::userdata_t& getUniform( const std::string& name );
|
||||
uf::Serializer getUniformJson( const std::string& name, bool cache = true );
|
||||
ext::vulkan::userdata_t getUniformUserdata( const std::string& name, const ext::json::Value& payload );
|
||||
bool updateUniform( const std::string& name );
|
||||
bool updateUniform( const std::string& name, const ext::vulkan::userdata_t& );
|
||||
bool updateUniform( const std::string& name, const ext::json::Value& payload );
|
||||
|
||||
bool hasStorage( const std::string& name );
|
||||
Buffer* getStorageBuffer( const std::string& name );
|
||||
uf::Serializer getStorageJson( const std::string& name, bool cache = true );
|
||||
ext::vulkan::userdata_t getStorageUserdata( const std::string& name, const ext::json::Value& payload );
|
||||
};
|
||||
struct UF_API Pipeline {
|
||||
bool aliased = false;
|
||||
|
||||
|
||||
61
engine/inc/uf/ext/vulkan/shader.h
Normal file
61
engine/inc/uf/ext/vulkan/shader.h
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <uf/ext/vulkan/device.h>
|
||||
#include <uf/ext/vulkan/swapchain.h>
|
||||
#include <uf/ext/vulkan/initializers.h>
|
||||
#include <uf/ext/vulkan/texture.h>
|
||||
#include <uf/utils/graphic/descriptor.h>
|
||||
#include <uf/utils/graphic/mesh.h>
|
||||
|
||||
#define UF_GRAPHIC_POINTERED_USERDATA 1
|
||||
|
||||
namespace ext {
|
||||
namespace vulkan {
|
||||
#if UF_GRAPHIC_POINTERED_USERDATA
|
||||
typedef uf::PointeredUserdata userdata_t;
|
||||
#else
|
||||
typedef uf::Userdata userdata_t;
|
||||
#endif
|
||||
|
||||
ext::json::Value definitionToJson(/*const*/ ext::json::Value& definition );
|
||||
ext::vulkan::userdata_t jsonToUserdata( const ext::json::Value& payload, const ext::json::Value& definition );
|
||||
|
||||
struct UF_API Shader : public Buffers {
|
||||
bool aliased = false;
|
||||
|
||||
Device* device = NULL;
|
||||
std::string filename = "";
|
||||
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
VkPipelineShaderStageCreateInfo descriptor;
|
||||
std::vector<VkDescriptorSetLayoutBinding> descriptorSetLayoutBindings;
|
||||
std::vector<VkSpecializationMapEntry> specializationMapEntries;
|
||||
VkSpecializationInfo specializationInfo;
|
||||
|
||||
uf::Serializer metadata;
|
||||
|
||||
ext::vulkan::userdata_t specializationConstants;
|
||||
std::vector<ext::vulkan::userdata_t> pushConstants;
|
||||
std::vector<ext::vulkan::userdata_t> uniforms;
|
||||
// ~Shader();
|
||||
void initialize( Device& device, const std::string&, VkShaderStageFlagBits );
|
||||
void destroy();
|
||||
bool validate();
|
||||
|
||||
bool hasUniform( const std::string& name );
|
||||
|
||||
Buffer* getUniformBuffer( const std::string& name );
|
||||
ext::vulkan::userdata_t& getUniform( const std::string& name );
|
||||
uf::Serializer getUniformJson( const std::string& name, bool cache = true );
|
||||
ext::vulkan::userdata_t getUniformUserdata( const std::string& name, const ext::json::Value& payload );
|
||||
bool updateUniform( const std::string& name );
|
||||
bool updateUniform( const std::string& name, const ext::vulkan::userdata_t& );
|
||||
bool updateUniform( const std::string& name, const ext::json::Value& payload );
|
||||
|
||||
bool hasStorage( const std::string& name );
|
||||
Buffer* getStorageBuffer( const std::string& name );
|
||||
uf::Serializer getStorageJson( const std::string& name, bool cache = true );
|
||||
ext::vulkan::userdata_t getStorageUserdata( const std::string& name, const ext::json::Value& payload );
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ namespace ext {
|
||||
Device* device = NULL;
|
||||
|
||||
VkSampler sampler;
|
||||
struct {
|
||||
struct Descriptor {
|
||||
struct {
|
||||
VkFilter min = VK_FILTER_LINEAR;
|
||||
VkFilter mag = VK_FILTER_LINEAR;
|
||||
@ -38,7 +38,11 @@ namespace ext {
|
||||
|
||||
void initialize( Device& device );
|
||||
void destroy();
|
||||
|
||||
static std::vector<ext::vulkan::Sampler> samplers;
|
||||
static ext::vulkan::Sampler retrieve( const Descriptor& info );
|
||||
};
|
||||
|
||||
struct UF_API Texture {
|
||||
Device* device = nullptr;
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <uf/config.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
namespace uf {
|
||||
namespace io {
|
||||
@ -15,8 +16,19 @@ namespace uf {
|
||||
std::string UF_API extension( const std::string&, int32_t );
|
||||
std::string UF_API directory( const std::string& );
|
||||
std::string UF_API sanitize( const std::string&, const std::string& = "" );
|
||||
|
||||
std::string UF_API readAsString( const std::string&, const std::string& = "" );
|
||||
std::vector<uint8_t> UF_API readAsBuffer( const std::string&, const std::string& = "" );
|
||||
|
||||
size_t UF_API write( const std::string& filename, const void*, size_t = SIZE_MAX );
|
||||
|
||||
template<typename T> inline size_t write( const std::string& filename, const std::vector<T>& buffer, size_t size = SIZE_MAX ) {
|
||||
return write( filename, buffer.data(), std::min( buffer.size(), size ) );
|
||||
}
|
||||
inline size_t write( const std::string& filename, const std::string& string, size_t size = SIZE_MAX ) {
|
||||
return write( filename, string.c_str(), std::min( string.size(), size ) );
|
||||
}
|
||||
|
||||
std::string UF_API hash( const std::string& );
|
||||
bool UF_API exists( const std::string& );
|
||||
size_t UF_API mtime( const std::string& );
|
||||
|
||||
0
engine/inc/uf/utils/io/file.inl
Normal file
0
engine/inc/uf/utils/io/file.inl
Normal file
@ -25,7 +25,6 @@ namespace {
|
||||
uf::iostream << "HTTP Error " << http.code << " on GET " << url << "\n";
|
||||
return false;
|
||||
}
|
||||
std::ofstream output;
|
||||
|
||||
std::string actual = hash;
|
||||
if ( hash != "" && (actual = uf::string::sha256(url)) != hash ) {
|
||||
@ -33,9 +32,7 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
output.open("./" + filename, std::ios::binary);
|
||||
output.write( http.response.c_str(), http.response.size() );
|
||||
output.close();
|
||||
uf::io::write( "./" + filename, http.response );
|
||||
return true;
|
||||
}
|
||||
std::string hashed( const std::string& uri ) {
|
||||
@ -103,7 +100,7 @@ std::string uf::Asset::cache( const std::string& uri, const std::string& hash, c
|
||||
std::string extension = uf::io::extension( uri );
|
||||
if ( uri.substr(0,5) == "https" ) {
|
||||
std::string hash = hashed( uri );
|
||||
std::string cached = uf::io::root + "/cache/" + hash + "." + extension;
|
||||
std::string cached = uf::io::root + "/cache/http/" + hash + "." + extension;
|
||||
if ( !uf::io::exists( cached ) && !retrieve( uri, cached, hash ) ) {
|
||||
uf::iostream << "Failed to preload `" + uri + "` (`" + cached + "`): HTTP error" << "\n";
|
||||
return "";
|
||||
@ -126,7 +123,7 @@ std::string uf::Asset::load( const std::string& uri, const std::string& hash, co
|
||||
std::string extension = uf::io::extension( uri );
|
||||
if ( uri.substr(0,5) == "https" ) {
|
||||
std::string hash = hashed( uri );
|
||||
std::string cached = uf::io::root + "/cache/" + hash + "." + extension;
|
||||
std::string cached = uf::io::root + "/cache/http/" + hash + "." + extension;
|
||||
if ( !uf::io::exists( cached ) && !retrieve( uri, cached, hash ) ) {
|
||||
uf::iostream << "Failed to load `" + uri + "` (`" + cached + "`): HTTP error" << "\n";
|
||||
return "";
|
||||
|
||||
@ -386,7 +386,7 @@ void UF_API ext::bullet::debugDraw( uf::Object& object ) {
|
||||
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::NONE;
|
||||
|
||||
graphic.material.attachShader(uf::io::root + "/shaders/base/colored.vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root + "/shaders/base/frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
graphic.material.attachShader(uf::io::root + "/shaders/base/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
|
||||
graphic.initialize();
|
||||
graphic.initializeMesh( mesh, 0 );
|
||||
|
||||
@ -98,7 +98,7 @@ namespace {
|
||||
};
|
||||
auto& specializationConstants = shader.specializationConstants.get<SpecializationConstant>();
|
||||
specializationConstants.textures = texture2Ds;
|
||||
specializationConstants.cascades = texture3Ds / 4;
|
||||
specializationConstants.cascades = texture3Ds / 4; // 5;
|
||||
ext::json::forEach( shader.metadata["specializationConstants"], [&]( ext::json::Value& sc ){
|
||||
std::string name = sc["name"].as<std::string>();
|
||||
if ( name == "TEXTURES" ) sc["value"] = specializationConstants.textures;
|
||||
@ -114,6 +114,7 @@ namespace {
|
||||
else if ( name == "voxelUv" ) layout.descriptorCount = specializationConstants.cascades;
|
||||
else if ( name == "voxelNormal" ) layout.descriptorCount = specializationConstants.cascades;
|
||||
else if ( name == "voxelRadiance" ) layout.descriptorCount = specializationConstants.cascades;
|
||||
// else if ( name == "voxelDepth" ) layout.descriptorCount = specializationConstants.cascades;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -154,6 +155,7 @@ namespace {
|
||||
for ( auto& t : sceneTextures.voxels.uv ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.normal ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radiance ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
// for ( auto& t : sceneTextures.voxels.depth ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
}
|
||||
}
|
||||
if ( graph.metadata["flags"]["LOAD"].as<bool>() ) {
|
||||
|
||||
@ -143,9 +143,10 @@ namespace UL {
|
||||
else if ( s == "Middle" ) button = ULMouseButton::kMouseButton_Middle;
|
||||
else if ( s == "Right" ) button = ULMouseButton::kMouseButton_Right;
|
||||
}
|
||||
int x = event["mouse"]["position"]["x"].as<int>() / ext::ultralight::scale;
|
||||
int y = event["mouse"]["position"]["y"].as<int>() / ext::ultralight::scale;
|
||||
handle = ulCreateMouseEvent( type, x, y, button );
|
||||
auto mouse = uf::vector::decode( event["mouse"]["position"], pod::Vector2i{} ) / ext::ultralight::scale;
|
||||
// int x = event["mouse"]["position"]["x"].as<int>() / ext::ultralight::scale;
|
||||
// int y = event["mouse"]["position"]["y"].as<int>() / ext::ultralight::scale;
|
||||
handle = ulCreateMouseEvent( type, mouse.x, mouse.y, button );
|
||||
}
|
||||
~MouseEvent() {
|
||||
if ( !handle ) return;
|
||||
|
||||
@ -6,16 +6,26 @@
|
||||
#include <uf/ext/vulkan/vulkan.h>
|
||||
#include <uf/ext/vulkan/device.h>
|
||||
|
||||
void ext::vulkan::Buffer::aliasBuffer( const ext::vulkan::Buffer& buffer ) {
|
||||
this->buffer = buffer.buffer;
|
||||
this->memory = buffer.memory;
|
||||
this->descriptor = buffer.descriptor;
|
||||
this->size = buffer.size;
|
||||
this->alignment = buffer.alignment;
|
||||
this->mapped = buffer.mapped;
|
||||
this->usage = buffer.usage;
|
||||
this->memoryProperties = buffer.memoryProperties;
|
||||
this->memAlloc = buffer.memAlloc;
|
||||
this->memReqs = buffer.memReqs;
|
||||
this->allocation = buffer.allocation;
|
||||
this->allocationInfo = buffer.allocationInfo;
|
||||
}
|
||||
|
||||
VkResult ext::vulkan::Buffer::map( VkDeviceSize size, VkDeviceSize offset ) {
|
||||
// return vkMapMemory( device, memory, offset, size, 0, &mapped );
|
||||
return vmaMapMemory( allocator, allocation, &mapped );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmap a mapped memory range
|
||||
*
|
||||
* @note Does not return a result as vkUnmapMemory can't fail
|
||||
*/
|
||||
void ext::vulkan::Buffer::unmap() {
|
||||
if ( !mapped ) return;
|
||||
// vkUnmapMemory( device, memory );
|
||||
@ -23,54 +33,23 @@ void ext::vulkan::Buffer::unmap() {
|
||||
mapped = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the allocated memory block to the buffer
|
||||
*
|
||||
* @param offset (Optional) Byte offset (from the beginning) for the memory region to bind
|
||||
*
|
||||
* @return VkResult of the bindBufferMemory call
|
||||
*/
|
||||
VkResult ext::vulkan::Buffer::bind( VkDeviceSize offset ) {
|
||||
// return vkBindBufferMemory( device, buffer, memory, offset );
|
||||
// return vmaBindBufferMemory( allocator, allocation, buffer );
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the default descriptor for this buffer
|
||||
*
|
||||
* @param size (Optional) Size of the memory range of the descriptor
|
||||
* @param offset (Optional) Byte offset from beginning
|
||||
*
|
||||
*/
|
||||
void ext::vulkan::Buffer::setupDescriptor( VkDeviceSize size, VkDeviceSize offset ) {
|
||||
descriptor.offset = offset;
|
||||
descriptor.buffer = buffer;
|
||||
descriptor.range = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified data to the mapped buffer
|
||||
*
|
||||
* @param data Pointer to the data to copy
|
||||
* @param size Size of the data to copy in machine units
|
||||
*
|
||||
*/
|
||||
void ext::vulkan::Buffer::copyTo( void* data, VkDeviceSize size ) {
|
||||
assert(mapped);
|
||||
memcpy(mapped, data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush a memory range of the buffer to make it visible to the device
|
||||
*
|
||||
* @note Only required for non-coherent memory
|
||||
*
|
||||
* @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range.
|
||||
* @param offset (Optional) Byte offset from beginning
|
||||
*
|
||||
* @return VkResult of the flush call
|
||||
*/
|
||||
VkResult ext::vulkan::Buffer::flush( VkDeviceSize size, VkDeviceSize offset ) {
|
||||
/*
|
||||
VkMappedMemoryRange mappedRange = {};
|
||||
@ -83,16 +62,6 @@ VkResult ext::vulkan::Buffer::flush( VkDeviceSize size, VkDeviceSize offset ) {
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate a memory range of the buffer to make it visible to the host
|
||||
*
|
||||
* @note Only required for non-coherent memory
|
||||
*
|
||||
* @param size (Optional) Size of the memory range to invalidate. Pass VK_WHOLE_SIZE to invalidate the complete buffer range.
|
||||
* @param offset (Optional) Byte offset from beginning
|
||||
*
|
||||
* @return VkResult of the invalidate call
|
||||
*/
|
||||
VkResult ext::vulkan::Buffer::invalidate( VkDeviceSize size, VkDeviceSize offset ) {
|
||||
/*
|
||||
VkMappedMemoryRange mappedRange = {};
|
||||
@ -130,6 +99,8 @@ void ext::vulkan::Buffer::initialize( VkDevice device ) {
|
||||
this->device = device;
|
||||
}
|
||||
void ext::vulkan::Buffer::destroy() {
|
||||
if ( !device ) return;
|
||||
|
||||
if ( buffer ) {
|
||||
vmaDestroyBuffer( allocator, buffer, allocation );
|
||||
// vkDestroyBuffer(device, buffer, nullptr);
|
||||
@ -155,7 +126,9 @@ ext::vulkan::Buffers::~Buffers() {
|
||||
}
|
||||
*/
|
||||
void ext::vulkan::Buffers::destroy() {
|
||||
for ( auto& buffer : buffers ) buffer.destroy();
|
||||
for ( auto& buffer : buffers ) {
|
||||
if ( buffer.device ) buffer.destroy();
|
||||
}
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
|
||||
@ -253,7 +253,7 @@ uint32_t ext::vulkan::Device::getMemoryType( uint32_t typeBits, VkMemoryProperty
|
||||
*memTypeFound = false;
|
||||
return 0;
|
||||
}
|
||||
// UF_EXCEPTION("Could not find a matching memory type");
|
||||
UF_EXCEPTION("Could not find a matching memory type");
|
||||
}
|
||||
|
||||
int ext::vulkan::Device::rate( VkPhysicalDevice device ) {
|
||||
@ -728,7 +728,7 @@ void ext::vulkan::Device::initialize() {
|
||||
queueInfo.pQueuePriorities = &defaultQueuePriority;
|
||||
queueCreateInfos.push_back(queueInfo);
|
||||
} else {
|
||||
queueFamilyIndices.graphics = VK_NULL_HANDLE;
|
||||
queueFamilyIndices.graphics = NULL; // VK_NULL_HANDLE;
|
||||
}
|
||||
// Dedicated compute queue
|
||||
if ( requestedQueueTypes & VK_QUEUE_COMPUTE_BIT ) {
|
||||
@ -782,16 +782,31 @@ void ext::vulkan::Device::initialize() {
|
||||
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
|
||||
}
|
||||
|
||||
if ( vkCreateDevice( this->physicalDevice, &deviceCreateInfo, nullptr, &this->logicalDevice) != VK_SUCCESS )
|
||||
VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{};
|
||||
VkPhysicalDeviceDescriptorIndexingFeatures descriptorIndexingFeatures{};
|
||||
{
|
||||
deviceCreateInfo.pEnabledFeatures = nullptr;
|
||||
deviceCreateInfo.pNext = &physicalDeviceFeatures2;
|
||||
}
|
||||
{
|
||||
physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
physicalDeviceFeatures2.features = enabledFeatures;
|
||||
physicalDeviceFeatures2.pNext = &descriptorIndexingFeatures;
|
||||
}
|
||||
{
|
||||
descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
|
||||
descriptorIndexingFeatures.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
|
||||
descriptorIndexingFeatures.shaderStorageImageArrayNonUniformIndexing = VK_TRUE;
|
||||
descriptorIndexingFeatures.runtimeDescriptorArray = VK_TRUE;
|
||||
descriptorIndexingFeatures.descriptorBindingVariableDescriptorCount = VK_TRUE;
|
||||
}
|
||||
|
||||
if ( vkCreateDevice( this->physicalDevice, &deviceCreateInfo, nullptr, &this->logicalDevice) != VK_SUCCESS ) {
|
||||
UF_EXCEPTION("failed to create logical device!");
|
||||
|
||||
|
||||
|
||||
}
|
||||
{
|
||||
uf::Serializer payload = ext::json::array();
|
||||
for ( auto* c_str : deviceExtensions ) {
|
||||
payload.emplace_back( std::string(c_str) );
|
||||
}
|
||||
for ( auto* c_str : deviceExtensions ) payload.emplace_back( std::string(c_str) );
|
||||
uf::hooks.call("vulkan:Device.ExtensionsEnabled", payload);
|
||||
}
|
||||
{
|
||||
@ -911,6 +926,15 @@ void ext::vulkan::Device::initialize() {
|
||||
{
|
||||
VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};
|
||||
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||
|
||||
std::vector<uint8_t> buffer;
|
||||
// read from cache on disk
|
||||
if ( uf::io::exists( uf::io::root + "/cache/vulkan/cache.bin" ) ) {
|
||||
buffer = uf::io::readAsBuffer( uf::io::root + "/cache/vulkan/cache.bin" );
|
||||
pipelineCacheCreateInfo.initialDataSize = buffer.size();
|
||||
pipelineCacheCreateInfo.pInitialData = buffer.data();
|
||||
}
|
||||
|
||||
VK_CHECK_RESULT(vkCreatePipelineCache( device, &pipelineCacheCreateInfo, nullptr, &this->pipelineCache));
|
||||
}
|
||||
// setup allocator
|
||||
@ -926,6 +950,17 @@ void ext::vulkan::Device::initialize() {
|
||||
|
||||
void ext::vulkan::Device::destroy() {
|
||||
if ( this->pipelineCache ) {
|
||||
// write cache on disk
|
||||
{
|
||||
size_t size{};
|
||||
VK_CHECK_RESULT(vkGetPipelineCacheData(this->logicalDevice, this->pipelineCache, &size, NULL));
|
||||
|
||||
std::vector<uint8_t> data(size);
|
||||
VK_CHECK_RESULT(vkGetPipelineCacheData(this->logicalDevice, this->pipelineCache, &size, data.data()));
|
||||
|
||||
uf::io::write( uf::io::root + "/cache/vulkan/cache.bin", data );
|
||||
}
|
||||
|
||||
vkDestroyPipelineCache( this->logicalDevice, this->pipelineCache, nullptr );
|
||||
this->pipelineCache = nullptr;
|
||||
}
|
||||
|
||||
@ -17,787 +17,13 @@
|
||||
namespace {
|
||||
uint32_t VERTEX_BUFFER_BIND_ID = 0;
|
||||
}
|
||||
ext::json::Value ext::vulkan::definitionToJson(/*const*/ ext::json::Value& definition ) {
|
||||
ext::json::Value member;
|
||||
// is object
|
||||
if ( !ext::json::isNull(definition["members"]) ) {
|
||||
ext::json::forEach(definition["members"], [&](/*const*/ ext::json::Value& value){
|
||||
std::string key = uf::string::split(value["name"].as<std::string>(), " ").back();
|
||||
member[key] = ext::vulkan::definitionToJson(value);
|
||||
});
|
||||
// is primitive
|
||||
} else if ( !ext::json::isNull(definition["value"]) ) {
|
||||
// is array of structs
|
||||
if ( definition["struct"].as<bool>() ) {
|
||||
ext::json::forEach(definition["value"], [&](/*const*/ ext::json::Value& value){
|
||||
ext::json::Value parsed;
|
||||
parsed["name"] = definition["name"];
|
||||
parsed["size"] = definition["size"];
|
||||
parsed["struct"] = definition["struct"];
|
||||
parsed["members"] = value;
|
||||
parsed = ext::vulkan::definitionToJson(parsed);
|
||||
member.emplace_back(parsed);
|
||||
});
|
||||
} else {
|
||||
member = definition["value"];
|
||||
}
|
||||
}
|
||||
return member;
|
||||
}
|
||||
ext::vulkan::userdata_t ext::vulkan::jsonToUserdata( const ext::json::Value& payload, const ext::json::Value& definition ) {
|
||||
size_t bufferLen = definition["size"].as<size_t>();
|
||||
|
||||
ext::vulkan::userdata_t userdata;
|
||||
userdata.create(bufferLen);
|
||||
|
||||
uint8_t* byteBuffer = (uint8_t*) (void*) userdata;
|
||||
uint8_t* byteBufferStart = byteBuffer;
|
||||
uint8_t* byteBufferEnd = byteBuffer + bufferLen;
|
||||
|
||||
#if UF_JSON_NLOHMANN_ORDERED
|
||||
// JSON is ordered, we can just push directly
|
||||
#define UF_SHADER_TRACK_NAMES 0
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
std::vector<std::string> variableName;
|
||||
#endif
|
||||
std::function<void(const ext::json::Value&)> parse = [&]( const ext::json::Value& value ){
|
||||
// is array or object
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( ext::json::isObject(value) ) {
|
||||
ext::json::forEach(value, [&]( const std::string& name, const ext::json::Value& member ){
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
variableName.emplace_back(name);
|
||||
#endif
|
||||
parse(member);
|
||||
});
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if ( ext::json::isArray(value) ) {
|
||||
ext::json::forEach(value, [&]( size_t i, const ext::json::Value& element ){
|
||||
variableName.emplace_back("["+std::to_string(i)+"]");
|
||||
parse(element);
|
||||
});
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if ( ext::json::isArray(value) || ext::json::isObject(value) ) {
|
||||
ext::json::forEach(value, parse);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
std::string path = uf::string::join(variableName, ".");
|
||||
path = uf::string::replace( path, ".[", "[" );
|
||||
VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = " << value.dump());
|
||||
#endif
|
||||
// is strictly an int
|
||||
if ( value.is<int>(true) ) {
|
||||
size_t size = sizeof(int32_t);
|
||||
auto get = value.as<int32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
// is strictly an unsigned int
|
||||
} else if ( value.is<size_t>(true) ) {
|
||||
size_t size = sizeof(uint32_t);
|
||||
auto get = value.as<uint32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
// is strictly a float
|
||||
} else if ( value.is<float>(true) ) {
|
||||
size_t size = sizeof(float);
|
||||
auto get = value.as<float>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
}
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
};
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Updating " << name << " in " << filename);
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
parse(payload);
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
#else
|
||||
auto pushValue = [&]( const std::string& primitive, const ext::json::Value& input ){
|
||||
if ( primitive == "bool" ) {
|
||||
size_t size = sizeof(bool); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<bool>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int8_t" ) {
|
||||
size_t size = sizeof(int8_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
|
||||
// auto get = input.as<int8_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "uint8_t" ) {
|
||||
size_t size = sizeof(uint8_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<uint8_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int16_t" ) {
|
||||
size_t size = sizeof(int16_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<int16_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "uint16_t" ) {
|
||||
size_t size = sizeof(uint16_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<uint16_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int32_t" ) {
|
||||
size_t size = sizeof(int32_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<int32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
}
|
||||
else if ( primitive == "uint32_t" ) {
|
||||
size_t size = sizeof(uint32_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<int32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int64_t" ) {
|
||||
size_t size = sizeof(int64_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<uint64_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "uint64_t" ) {
|
||||
size_t size = sizeof(uint64_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<uint64_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "half" ) {
|
||||
size_t size = sizeof(float); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<float>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "float" ) {
|
||||
size_t size = sizeof(float); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<float>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "double" ) {
|
||||
auto get = input.as<double>();
|
||||
size_t size = sizeof(double); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#define UF_SHADER_TRACK_NAMES 0
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
bool SKIP_ADD = false;
|
||||
std::vector<std::string> variableName;
|
||||
#endif
|
||||
std::function<void(const ext::json::Value&, const ext::json::Value&)> parseDefinition = [&](const ext::json::Value& input, const ext::json::Value& definition ){
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( SKIP_ADD ) {
|
||||
SKIP_ADD = false;
|
||||
} else {
|
||||
auto split = uf::string::split(definition["name"].as<std::string>(), " ");
|
||||
std::string type = split.front();
|
||||
std::string name = split.back();
|
||||
variableName.emplace_back(name);
|
||||
}
|
||||
#endif
|
||||
// is object
|
||||
if ( !ext::json::isNull(definition["members"]) ) {
|
||||
ext::json::forEach(definition["members"], [&](const ext::json::Value& member){
|
||||
std::string key = uf::string::split(member["name"].as<std::string>(), " ").back();
|
||||
parseDefinition(input[key], member);
|
||||
});
|
||||
// is array or primitive
|
||||
} else if ( !ext::json::isNull(definition["value"]) ) {
|
||||
// is object
|
||||
auto split = uf::string::split(definition["name"].as<std::string>(), " ");
|
||||
std::string type = split.front();
|
||||
std::string name = split.back();
|
||||
std::regex regex("^(?:(.+?)\\<)?(.+?)(?:\\>)?(?:\\[(\\d+)\\])?$");
|
||||
std::smatch match;
|
||||
if ( !std::regex_search( type, match, regex ) ) {
|
||||
std::cout << "Ill formatted typename: " << definition["name"].as<std::string>() << std::endl;
|
||||
return;
|
||||
}
|
||||
std::string vectorMatrix = match[1].str();
|
||||
std::string primitive = match[2].str();
|
||||
std::string arraySize = match[3].str();
|
||||
if ( ext::json::isObject(input) ) {
|
||||
ext::json::Value cloned;
|
||||
cloned["name"] = definition["name"];
|
||||
cloned["size"] = definition["size"].as<size_t>() / definition["value"].size();
|
||||
cloned["members"] = definition["value"][0];
|
||||
parseDefinition( input, cloned );
|
||||
}
|
||||
// is array
|
||||
else if ( ext::json::isArray(input) ) {
|
||||
ext::json::forEach( input, [&]( size_t i, const ext::json::Value& value){
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
variableName.emplace_back("["+std::to_string(i)+"]");
|
||||
SKIP_ADD = true;
|
||||
#endif
|
||||
parseDefinition(input[i], definition);
|
||||
});
|
||||
}
|
||||
// is primitive
|
||||
else {
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
std::string path = uf::string::join(variableName, ".");
|
||||
path = uf::string::replace( path, ".[", "[" );
|
||||
VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = (" << primitive << ") " << input.dump());
|
||||
#endif
|
||||
pushValue( primitive, input );
|
||||
}
|
||||
}
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
};
|
||||
auto& definitions = metadata["definitions"]["uniforms"][name];
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Updating " << name << " in " << filename);
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
parseDefinition(payload, definitions);
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
#endif
|
||||
return userdata;
|
||||
}
|
||||
/*
|
||||
ext::vulkan::Shader::~Shader() {
|
||||
if ( !aliased ) destroy();
|
||||
}
|
||||
*/
|
||||
void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const std::string& filename, VkShaderStageFlagBits stage ) {
|
||||
this->device = &device;
|
||||
ext::vulkan::Buffers::initialize( device );
|
||||
aliased = false;
|
||||
|
||||
std::string spirv;
|
||||
|
||||
{
|
||||
std::ifstream is(this->filename = filename, std::ios::binary | std::ios::in | std::ios::ate);
|
||||
if ( !is.is_open() ) {
|
||||
VK_VALIDATION_MESSAGE("Error: Could not open shader file \"" << filename << "\"");
|
||||
return;
|
||||
}
|
||||
is.seekg(0, std::ios::end); spirv.reserve(is.tellg()); is.seekg(0, std::ios::beg);
|
||||
spirv.assign((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
|
||||
|
||||
assert(spirv.size() > 0);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
VkShaderModuleCreateInfo moduleCreateInfo = {};
|
||||
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
moduleCreateInfo.codeSize = spirv.size();
|
||||
moduleCreateInfo.pCode = (uint32_t*) spirv.data();
|
||||
|
||||
VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &module));
|
||||
}
|
||||
|
||||
{
|
||||
descriptor.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
descriptor.stage = stage;
|
||||
descriptor.module = module;
|
||||
descriptor.pName = "main";
|
||||
|
||||
assert(descriptor.module != VK_NULL_HANDLE);
|
||||
}
|
||||
// set up metadata
|
||||
{
|
||||
metadata["filename"] = filename;
|
||||
metadata["type"] = "";
|
||||
}
|
||||
// do reflection
|
||||
{
|
||||
spirv_cross::Compiler comp( (uint32_t*) &spirv[0], spirv.size() / 4 );
|
||||
spirv_cross::ShaderResources res = comp.get_shader_resources();
|
||||
|
||||
std::function<ext::json::Value(spirv_cross::TypeID)> parseMembers = [&]( spirv_cross::TypeID type_id ) {
|
||||
auto parseMember = [&]( auto type_id ){
|
||||
uf::Serializer payload;
|
||||
|
||||
auto type = comp.get_type(type_id);
|
||||
|
||||
std::string name = "";
|
||||
size_t size = 1;
|
||||
ext::json::Value value;
|
||||
switch ( type.basetype ) {
|
||||
case spirv_cross::SPIRType::BaseType::Boolean: name = "bool"; size = sizeof(bool); value = (bool) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::SByte: name = "int8_t"; size = sizeof(int8_t); value = (int8_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UByte: name = "uint8_t"; size = sizeof(uint8_t); value = (uint8_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Short: name = "int16_t"; size = sizeof(int16_t); value = (int16_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UShort: name = "uint16_t"; size = sizeof(uint16_t); value = (uint16_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Int: name = "int32_t"; size = sizeof(int32_t); value = (int32_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UInt: name = "uint32_t"; size = sizeof(uint32_t); value = (uint32_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Int64: name = "int64_t"; size = sizeof(int64_t); value = (int64_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UInt64: name = "uint64_t"; size = sizeof(uint64_t); value = (uint64_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Half: name = "half"; size = sizeof(float)/2; value = (float) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Float: name = "float"; size = sizeof(float); value = (float) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Double: name = "double"; size = sizeof(double); value = (double) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Image: name = "image2D"; break;
|
||||
case spirv_cross::SPIRType::BaseType::SampledImage: name = "sampler2D"; break;
|
||||
case spirv_cross::SPIRType::BaseType::Sampler: name = "sampler"; break;
|
||||
default:
|
||||
name = comp.get_name(type_id);
|
||||
size = comp.get_declared_struct_size(type);
|
||||
break;
|
||||
}
|
||||
if ( name == "" ) name = comp.get_name(type.type_alias);
|
||||
if ( name == "" ) name = comp.get_name(type.type_alias);
|
||||
if ( name == "" ) name = comp.get_name(type.parent_type);
|
||||
if ( name == "" ) name = comp.get_fallback_name(type_id);
|
||||
if ( type.vecsize > 1 ) {
|
||||
name = "<"+name+">";
|
||||
if ( type.columns > 1 ) {
|
||||
name = "Matrix"+std::to_string(type.vecsize)+"x"+std::to_string(type.columns)+name;
|
||||
} else {
|
||||
name = "Vector"+std::to_string(type.vecsize)+name;
|
||||
}
|
||||
}
|
||||
{
|
||||
ext::json::Value source = value;
|
||||
value = ext::json::array();
|
||||
for ( size_t i = 0; i < type.vecsize * type.columns; ++i ) {
|
||||
value.emplace_back(source);
|
||||
}
|
||||
size *= type.columns * type.vecsize;
|
||||
}
|
||||
for ( auto arraySize : type.array ) {
|
||||
if ( arraySize > 1 ) {
|
||||
ext::json::Value source = value;
|
||||
value = ext::json::array();
|
||||
for ( size_t i = 0; i < arraySize; ++i ) {
|
||||
value.emplace_back(source);
|
||||
}
|
||||
name += "[" + std::to_string(arraySize) + "]";
|
||||
size *= arraySize;
|
||||
}
|
||||
}
|
||||
if ( ext::json::isArray(value) && value.size() == 1 ) {
|
||||
value = value[0];
|
||||
}
|
||||
payload["name"] = name;
|
||||
payload["size"] = size;
|
||||
payload["value"] = value;
|
||||
return payload;
|
||||
};
|
||||
uf::Serializer payload = ext::json::array();
|
||||
const auto& type = comp.get_type(type_id);
|
||||
for ( auto& member_type_id : type.member_types ) {
|
||||
const auto& member_type = comp.get_type(member_type_id);
|
||||
std::string name = comp.get_member_name(type.type_alias, payload.size());
|
||||
if ( name == "" ) name = comp.get_member_name(type.parent_type, payload.size());
|
||||
if ( name == "" ) name = comp.get_member_name(type_id, payload.size());
|
||||
|
||||
auto& entry = payload.emplace_back();
|
||||
auto parsed = parseMember(member_type_id);
|
||||
std::string type_name = parsed["name"];
|
||||
entry["name"] = type_name + " " + name;
|
||||
if ( member_type.basetype == spirv_cross::SPIRType::BaseType::Struct ) {
|
||||
entry["struct"] = true;
|
||||
auto parsed = parseMembers(member_type_id);
|
||||
if ( !member_type.array.empty() && member_type.array[0] > 1 ) {
|
||||
size_t size = comp.get_declared_struct_size(member_type);
|
||||
for ( auto arraySize : member_type.array ) {
|
||||
ext::json::Value source = parsed;
|
||||
parsed = ext::json::array();
|
||||
for ( size_t i = 0; i < arraySize; ++i ) {
|
||||
parsed.emplace_back(source);
|
||||
}
|
||||
size = size * arraySize;
|
||||
}
|
||||
entry["size"] = size;
|
||||
entry["value"] = parsed;
|
||||
} else {
|
||||
entry["size"] = comp.get_declared_struct_size(member_type);
|
||||
entry["members"] = parsed;
|
||||
}
|
||||
} else {
|
||||
entry["size"] = parsed["size"];
|
||||
entry["value"] = parsed["value"];
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
};
|
||||
|
||||
auto parseResource = [&]( const spirv_cross::Resource& resource, VkDescriptorType descriptorType, size_t index ) {
|
||||
const auto& type = comp.get_type(resource.type_id);
|
||||
const auto& base_type = comp.get_type(resource.base_type_id);
|
||||
std::string name = resource.name;
|
||||
size_t arraySize = 1;
|
||||
if ( !type.array.empty() ) {
|
||||
arraySize = type.array[0];
|
||||
}
|
||||
|
||||
switch ( descriptorType ) {
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {
|
||||
std::string tname = "";
|
||||
switch ( type.image.dim ) {
|
||||
case spv::Dim::Dim1D: tname = "1D"; break;
|
||||
case spv::Dim::Dim2D: tname = "2D"; break;
|
||||
case spv::Dim::Dim3D: tname = "3D"; break;
|
||||
case spv::Dim::DimCube: tname = "Cube"; break;
|
||||
case spv::Dim::DimRect: tname = "Rect"; break;
|
||||
case spv::Dim::DimBuffer: tname = "Buffer"; break;
|
||||
case spv::Dim::DimSubpassData: tname = "SubpassData"; break;
|
||||
|
||||
}
|
||||
size_t binding = comp.get_decoration(resource.id, spv::DecorationBinding);
|
||||
std::string key = std::to_string(binding);
|
||||
metadata["definitions"]["textures"][key]["name"] = name;
|
||||
metadata["definitions"]["textures"][key]["index"] = index;
|
||||
metadata["definitions"]["textures"][key]["binding"] = binding;
|
||||
metadata["definitions"]["textures"][key]["size"] = arraySize;
|
||||
metadata["definitions"]["textures"][key]["type"] = tname;
|
||||
} break;
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: {
|
||||
size_t bufferSize = comp.get_declared_struct_size(base_type);
|
||||
if ( bufferSize <= 0 ) break;
|
||||
if ( bufferSize > device.properties.limits.maxUniformBufferRange ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer length of " << bufferSize << " for shader " << filename);
|
||||
bufferSize = device.properties.limits.maxUniformBufferRange;
|
||||
}
|
||||
size_t misalignment = bufferSize % device.properties.limits.minStorageBufferOffsetAlignment;
|
||||
if ( misalignment != 0 ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer alignment of " << misalignment << " for shader " << filename << ", correcting...");
|
||||
bufferSize += misalignment;
|
||||
}
|
||||
{
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Uniform size of " << bufferSize << " for shader " << filename);
|
||||
auto& uniform = uniforms.emplace_back();
|
||||
uniform.create( bufferSize );
|
||||
}
|
||||
// generate definition to JSON
|
||||
{
|
||||
metadata["definitions"]["uniforms"][name]["name"] = name;
|
||||
metadata["definitions"]["uniforms"][name]["index"] = index;
|
||||
metadata["definitions"]["uniforms"][name]["size"] = bufferSize;
|
||||
metadata["definitions"]["uniforms"][name]["members"] = parseMembers(resource.type_id);
|
||||
}
|
||||
} break;
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {
|
||||
// generate definition to JSON
|
||||
{
|
||||
metadata["definitions"]["storage"][name]["name"] = name;
|
||||
metadata["definitions"]["storage"][name]["index"] = index;
|
||||
metadata["definitions"]["storage"][name]["members"] = parseMembers(resource.type_id);
|
||||
}
|
||||
// test
|
||||
{
|
||||
// updateUniform(name, getUniform(name));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
descriptorSetLayoutBindings.push_back( ext::vulkan::initializers::descriptorSetLayoutBinding( descriptorType, stage, comp.get_decoration(resource.id, spv::DecorationBinding), arraySize ) );
|
||||
};
|
||||
|
||||
|
||||
//for ( const auto& resource : res.key ) {
|
||||
#define LOOP_RESOURCES( key, type ) for ( size_t i = 0; i < res.key.size(); ++i ) {\
|
||||
const auto& resource = res.key[i];\
|
||||
VK_DEBUG_VALIDATION_MESSAGE("["<<filename<<"] Found resource: "#type " with binding: " << comp.get_decoration(resource.id, spv::DecorationBinding));\
|
||||
parseResource( resource, type, i );\
|
||||
}
|
||||
LOOP_RESOURCES( sampled_images, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER );
|
||||
LOOP_RESOURCES( separate_images, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE );
|
||||
LOOP_RESOURCES( storage_images, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE );
|
||||
LOOP_RESOURCES( separate_samplers, VK_DESCRIPTOR_TYPE_SAMPLER );
|
||||
LOOP_RESOURCES( subpass_inputs, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT );
|
||||
LOOP_RESOURCES( uniform_buffers, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER );
|
||||
LOOP_RESOURCES( storage_buffers, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER );
|
||||
#undef LOOP_RESOURCES
|
||||
|
||||
for ( const auto& resource : res.push_constant_buffers ) {
|
||||
const auto& type = comp.get_type(resource.type_id);
|
||||
const auto& base_type = comp.get_type(resource.base_type_id);
|
||||
std::string name = resource.name;
|
||||
size_t size = comp.get_declared_struct_size(type);
|
||||
if ( size <= 0 ) continue;
|
||||
// not a multiple of 4, for some reason
|
||||
if ( size % 4 != 0 ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of " << size << " for shader " << filename << ", must be multiple of 4, correcting...");
|
||||
size /= 4;
|
||||
++size;
|
||||
size *= 4;
|
||||
}
|
||||
if ( size > device.properties.limits.maxPushConstantsSize ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of " << size << " for shader " << filename);
|
||||
size = device.properties.limits.maxPushConstantsSize;
|
||||
}
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Push constant size of " << size << " for shader " << filename);
|
||||
{
|
||||
auto& pushConstant = pushConstants.emplace_back();
|
||||
pushConstant.create( size );
|
||||
}
|
||||
// generate definition to JSON
|
||||
{
|
||||
metadata["definitions"]["pushConstants"][name]["name"] = name;
|
||||
metadata["definitions"]["pushConstants"][name]["size"] = size;
|
||||
metadata["definitions"]["pushConstants"][name]["index"] = pushConstants.size() - 1;
|
||||
metadata["definitions"]["pushConstants"][name]["members"] = parseMembers(resource.type_id);
|
||||
}
|
||||
}
|
||||
|
||||
size_t specializationSize = 0;
|
||||
for ( const auto& constant : comp.get_specialization_constants() ) {
|
||||
const auto& value = comp.get_constant(constant.id);
|
||||
const auto& type = comp.get_type(value.constant_type);
|
||||
size_t size = 4; //comp.get_declared_struct_size(type);
|
||||
|
||||
VkSpecializationMapEntry specializationMapEntry;
|
||||
specializationMapEntry.constantID = constant.constant_id;
|
||||
specializationMapEntry.size = size;
|
||||
specializationMapEntry.offset = specializationSize;
|
||||
specializationMapEntries.push_back(specializationMapEntry);
|
||||
specializationSize += size;
|
||||
}
|
||||
if ( specializationSize > 0 ) {
|
||||
specializationConstants.create( specializationSize );
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Specialization constants size of " << specializationSize << " for shader " << filename);
|
||||
|
||||
uint8_t* s = (uint8_t*) (void*) specializationConstants;
|
||||
size_t offset = 0;
|
||||
|
||||
metadata["specializationConstants"] = ext::json::array();
|
||||
for ( const auto& constant : comp.get_specialization_constants() ) {
|
||||
const auto& value = comp.get_constant(constant.id);
|
||||
const auto& type = comp.get_type(value.constant_type);
|
||||
std::string name = comp.get_name (constant.id);
|
||||
|
||||
ext::json::Value member;
|
||||
|
||||
size_t size = 4;
|
||||
uint8_t buffer[size];
|
||||
switch ( type.basetype ) {
|
||||
case spirv_cross::SPIRType::UInt: {
|
||||
auto v = value.scalar();
|
||||
member["type"] = "uint32_t";
|
||||
member["value"] = v;
|
||||
member["validate"] = true;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
case spirv_cross::SPIRType::Int: {
|
||||
auto v = value.scalar_i32();
|
||||
member["type"] = "int32_t";
|
||||
member["value"] = v;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
case spirv_cross::SPIRType::Float: {
|
||||
auto v = value.scalar_f32();
|
||||
member["type"] = "float";
|
||||
member["value"] = v;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
case spirv_cross::SPIRType::Boolean: {
|
||||
auto v = value.scalar()!=0;
|
||||
member["type"] = "bool";
|
||||
member["value"] = v;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
default: {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Unregistered specialization constant type at offset " << offset << " for shader " << filename );
|
||||
} break;
|
||||
}
|
||||
member["name"] = name;
|
||||
member["size"] = size;
|
||||
member["default"] = member["value"];
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Specialization constant: " << member["type"].as<std::string>() << " " << name << " = " << member["value"].dump() << "; at offset " << offset << " for shader " << filename );
|
||||
metadata["specializationConstants"].emplace_back(member);
|
||||
|
||||
memcpy( &s[offset], &buffer, size );
|
||||
offset += size;
|
||||
}
|
||||
/*
|
||||
{
|
||||
specializationInfo = {};
|
||||
specializationInfo.dataSize = specializationSize;
|
||||
specializationInfo.mapEntryCount = specializationMapEntries.size();
|
||||
specializationInfo.pMapEntries = specializationMapEntries.data();
|
||||
specializationInfo.pData = (void*) specializationConstants;
|
||||
descriptor.pSpecializationInfo = &specializationInfo;
|
||||
}
|
||||
*/
|
||||
}
|
||||
/*
|
||||
*/
|
||||
// LOOP_RESOURCES( stage_inputs, VkVertexInputAttributeDescription );
|
||||
// LOOP_RESOURCES( stage_outputs, VkPipelineColorBlendAttachmentState );
|
||||
}
|
||||
|
||||
// organize layouts
|
||||
{
|
||||
std::sort( descriptorSetLayoutBindings.begin(), descriptorSetLayoutBindings.end(), [&]( const VkDescriptorSetLayoutBinding& l, const VkDescriptorSetLayoutBinding& r ){
|
||||
return l.binding < r.binding;
|
||||
} );
|
||||
}
|
||||
|
||||
// update uniform buffers
|
||||
for ( auto& uniform : uniforms ) {
|
||||
auto& userdata = uniform.data();
|
||||
initializeBuffer(
|
||||
(void*) userdata.data,
|
||||
userdata.len,
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ext::vulkan::Shader::destroy() {
|
||||
if ( aliased ) return;
|
||||
if ( !device ) return;
|
||||
|
||||
ext::vulkan::Buffers::destroy();
|
||||
|
||||
if ( module != VK_NULL_HANDLE ) {
|
||||
vkDestroyShaderModule( *device, module, nullptr );
|
||||
module = VK_NULL_HANDLE;
|
||||
descriptor = {};
|
||||
}
|
||||
for ( auto& userdata : uniforms ) userdata.destroy();
|
||||
for ( auto& userdata : pushConstants ) userdata.destroy();
|
||||
uniforms.clear();
|
||||
pushConstants.clear();
|
||||
}
|
||||
|
||||
bool ext::vulkan::Shader::validate() {
|
||||
// check if uniforms match buffer size
|
||||
bool valid = true;
|
||||
{
|
||||
auto it = uniforms.begin();
|
||||
for ( auto& buffer : buffers ) {
|
||||
if ( !(buffer.usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) ) continue;
|
||||
if ( it == uniforms.end() ) break;
|
||||
auto& uniform = *(it++);
|
||||
if ( uniform.data().len != buffer.allocationInfo.size ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Uniform size mismatch: Expected " << buffer.allocationInfo.size << ", got " << uniform.data().len << "; fixing...");
|
||||
uniform.destroy();
|
||||
uniform.create(buffer.allocationInfo.size);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
bool ext::vulkan::Shader::hasUniform( const std::string& name ) {
|
||||
return !ext::json::isNull(metadata["definitions"]["uniforms"][name]);
|
||||
}
|
||||
ext::vulkan::Buffer* ext::vulkan::Shader::getUniformBuffer( const std::string& name ) {
|
||||
if ( !hasUniform(name) ) return NULL;
|
||||
size_t uniformIndex = metadata["definitions"]["uniforms"][name]["index"].as<size_t>();
|
||||
for ( size_t bufferIndex = 0, uniformCounter = 0; bufferIndex < buffers.size(); ++bufferIndex ) {
|
||||
if ( !(buffers[bufferIndex].usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) ) continue;
|
||||
if ( uniformCounter++ != uniformIndex ) continue;
|
||||
return &buffers[bufferIndex];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
ext::vulkan::userdata_t& ext::vulkan::Shader::getUniform( const std::string& name ) {
|
||||
if ( !hasUniform(name) ) {
|
||||
static ext::vulkan::userdata_t null;
|
||||
return null;
|
||||
}
|
||||
auto& definition = metadata["definitions"]["uniforms"][name];
|
||||
size_t uniformSize = definition["size"].as<size_t>();
|
||||
size_t uniformIndex = definition["index"].as<size_t>();
|
||||
auto& userdata = uniforms[uniformIndex];
|
||||
return userdata;
|
||||
}
|
||||
bool ext::vulkan::Shader::updateUniform( const std::string& name ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
auto& uniform = getUniform(name);
|
||||
return updateUniform(name, uniform);
|
||||
}
|
||||
bool ext::vulkan::Shader::updateUniform( const std::string& name, const ext::vulkan::userdata_t& userdata ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
auto* bufferObject = getUniformBuffer(name);
|
||||
if ( !bufferObject ) return false;
|
||||
size_t size = std::max(metadata["definitions"]["uniforms"][name]["size"].as<size_t>(), bufferObject->allocationInfo.size);
|
||||
updateBuffer( (void*) userdata, size, *bufferObject );
|
||||
return true;
|
||||
}
|
||||
|
||||
uf::Serializer ext::vulkan::Shader::getUniformJson( const std::string& name, bool cache ) {
|
||||
if ( !hasUniform(name) ) return ext::json::null();
|
||||
if ( cache && !ext::json::isNull(metadata["uniforms"][name]) ) return metadata["uniforms"][name];
|
||||
auto& definition = metadata["definitions"]["uniforms"][name];
|
||||
if ( cache ) return metadata["uniforms"][name] = definitionToJson(definition);
|
||||
return definitionToJson(definition);
|
||||
}
|
||||
ext::vulkan::userdata_t ext::vulkan::Shader::getUniformUserdata( const std::string& name, const ext::json::Value& payload ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
return jsonToUserdata(payload, metadata["definitions"]["uniforms"][name]);
|
||||
}
|
||||
bool ext::vulkan::Shader::updateUniform( const std::string& name, const ext::json::Value& payload ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
|
||||
auto* bufferObject = getUniformBuffer(name);
|
||||
if ( !bufferObject ) return false;
|
||||
|
||||
auto uniform = getUniformUserdata( name, payload );
|
||||
updateBuffer( (void*) uniform, uniform.data().len, *bufferObject );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ext::vulkan::Shader::hasStorage( const std::string& name ) {
|
||||
return !ext::json::isNull(metadata["definitions"]["storage"][name]);
|
||||
}
|
||||
|
||||
ext::vulkan::Buffer* ext::vulkan::Shader::getStorageBuffer( const std::string& name ) {
|
||||
if ( !hasStorage(name) ) return NULL;
|
||||
size_t storageIndex = metadata["definitions"]["storage"][name]["index"].as<size_t>();
|
||||
for ( size_t bufferIndex = 0, storageCounter = 0; bufferIndex < buffers.size(); ++bufferIndex ) {
|
||||
if ( !(buffers[bufferIndex].usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) ) continue;
|
||||
if ( storageCounter++ != storageIndex ) continue;
|
||||
return &buffers[bufferIndex];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
uf::Serializer ext::vulkan::Shader::getStorageJson( const std::string& name, bool cache ) {
|
||||
if ( !hasStorage(name) ) return ext::json::null();
|
||||
if ( cache && !ext::json::isNull(metadata["storage"][name]) ) return metadata["storage"][name];
|
||||
auto& definition = metadata["definitions"]["storage"][name];
|
||||
if ( cache ) return metadata["storage"][name] = definitionToJson(definition);
|
||||
return definitionToJson(definition);
|
||||
}
|
||||
ext::vulkan::userdata_t ext::vulkan::Shader::getStorageUserdata( const std::string& name, const ext::json::Value& payload ) {
|
||||
if ( !hasStorage(name) ) return false;
|
||||
return jsonToUserdata(payload, metadata["definitions"]["storage"][name]);
|
||||
}
|
||||
|
||||
void ext::vulkan::Pipeline::initialize( Graphic& graphic ) {
|
||||
return this->initialize( graphic, graphic.descriptor );
|
||||
}
|
||||
void ext::vulkan::Pipeline::initialize( Graphic& graphic, GraphicDescriptor& descriptor ) {
|
||||
this->device = graphic.device;
|
||||
//this->descriptor = descriptor;
|
||||
this->descriptor = descriptor;
|
||||
Device& device = *graphic.device;
|
||||
metadata["name"] = descriptor.pipeline;
|
||||
|
||||
@ -1123,11 +349,14 @@ void ext::vulkan::Pipeline::record( Graphic& graphic, VkCommandBuffer commandBuf
|
||||
vkCmdBindPipeline(commandBuffer, bindPoint, pipeline);
|
||||
}
|
||||
void ext::vulkan::Pipeline::update( Graphic& graphic ) {
|
||||
return this->update( graphic, graphic.descriptor );
|
||||
// return this->update( graphic, graphic.descriptor );
|
||||
return this->update( graphic, descriptor );
|
||||
}
|
||||
void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descriptor ) {
|
||||
//
|
||||
if ( descriptorSet == VK_NULL_HANDLE ) return;
|
||||
this->descriptor = descriptor;
|
||||
|
||||
//descriptor = d;
|
||||
RenderMode& renderMode = ext::vulkan::getRenderMode(descriptor.renderMode, true);
|
||||
auto& renderTarget = renderMode.getRenderTarget(descriptor.renderTarget );
|
||||
|
||||
@ -42,11 +42,11 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
ext::vulkan::RenderMode::initialize( device );
|
||||
renderTarget.device = &device;
|
||||
size_t eyes = metadata["eyes"].as<size_t>();
|
||||
size_t msaa = ext::vulkan::settings::msaa;
|
||||
for ( size_t eye = 0; eye < eyes; ++eye ) {
|
||||
struct {
|
||||
size_t id, normals, uvs, albedo, depth, output, debug;
|
||||
} attachments;
|
||||
size_t msaa = ext::vulkan::settings::msaa;
|
||||
|
||||
attachments.id = renderTarget.attach(RenderTarget::Attachment::Descriptor{
|
||||
/*.format = */VK_FORMAT_R16G16_UINT,
|
||||
@ -122,7 +122,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
});
|
||||
}
|
||||
metadata["outputs"].emplace_back(attachments.output);
|
||||
|
||||
#if 0
|
||||
attachments.debug = renderTarget.attach(RenderTarget::Attachment::Descriptor{
|
||||
/*.format = */VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
/*.layout = */VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
@ -130,6 +130,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
/*.blend = */false,
|
||||
/*.samples = */1,
|
||||
});
|
||||
#endif
|
||||
|
||||
if ( ext::vulkan::settings::experimental::deferredMode != "" ) {
|
||||
// First pass: fill the G-Buffer
|
||||
@ -147,7 +148,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
{
|
||||
renderTarget.addPass(
|
||||
/*.*/ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
|
||||
/*.colors =*/ { attachments.output, attachments.debug },
|
||||
/*.colors =*/ { attachments.output },
|
||||
/*.inputs =*/ { attachments.id, attachments.normals, attachments.uvs, attachments.depth },
|
||||
/*.resolve =*/ {},
|
||||
/*.depth = */ attachments.depth,
|
||||
@ -170,7 +171,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
{
|
||||
renderTarget.addPass(
|
||||
/*.*/ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
|
||||
/*.colors =*/ { attachments.output, attachments.debug },
|
||||
/*.colors =*/ { attachments.output },
|
||||
/*.inputs =*/ { attachments.id, attachments.normals, attachments.albedo, attachments.depth },
|
||||
/*.resolve =*/ {},
|
||||
/*.depth = */ attachments.depth,
|
||||
@ -198,17 +199,14 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
|
||||
blitter.initialize( this->getName() );
|
||||
blitter.initializeMesh( mesh );
|
||||
std::string fragmentShaderFilename = ( msaa <= 1 ) ? "no-msaa." : "";
|
||||
if ( ext::vulkan::settings::experimental::deferredMode == "vxgi" ) {
|
||||
blitter.material.initializeShaders({
|
||||
{uf::io::root+"/shaders/display/subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT},
|
||||
{uf::io::root+"/shaders/display/subpass.vxgi.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT}
|
||||
});
|
||||
} else {
|
||||
blitter.material.initializeShaders({
|
||||
{uf::io::root+"/shaders/display/subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT},
|
||||
{uf::io::root+"/shaders/display/subpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT}
|
||||
});
|
||||
fragmentShaderFilename = ( msaa <= 1 ) ? "vxgi.no-msaa." : "vxgi.";
|
||||
}
|
||||
blitter.material.initializeShaders({
|
||||
{uf::io::root+"/shaders/display/subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT},
|
||||
{uf::io::root+"/shaders/display/subpass." + fragmentShaderFilename + "frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT}
|
||||
});
|
||||
{
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
|
||||
@ -220,8 +218,8 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
|
||||
if ( ext::vulkan::settings::experimental::deferredMode == "vxgi" ) {
|
||||
struct SpecializationConstant {
|
||||
uint32_t maxCascades = 16;
|
||||
uint32_t maxTextures = 512;
|
||||
uint32_t maxCascades = 16;
|
||||
};
|
||||
auto& specializationConstants = shader.specializationConstants.get<SpecializationConstant>();
|
||||
|
||||
@ -287,13 +285,13 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
|
||||
);
|
||||
}
|
||||
blitter.initializePipeline();
|
||||
// blitter.initializePipeline();
|
||||
for ( size_t eye = 0; eye < eyes; ++eye ) {
|
||||
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
|
||||
descriptor.subpass = 2 * eye + 1;
|
||||
if ( blitter.hasPipeline( descriptor ) ) continue;
|
||||
blitter.initializePipeline( descriptor );
|
||||
blitter.descriptor.subpass = 2 * eye + 1;
|
||||
if ( blitter.hasPipeline( blitter.descriptor ) ) continue;
|
||||
blitter.initializePipeline( blitter.descriptor );
|
||||
}
|
||||
blitter.descriptor.subpass = 1;
|
||||
}
|
||||
}
|
||||
void ext::vulkan::DeferredRenderMode::tick() {
|
||||
@ -303,7 +301,14 @@ void ext::vulkan::DeferredRenderMode::tick() {
|
||||
renderTarget.initialize( *renderTarget.device );
|
||||
// update blitter descriptor set
|
||||
if ( blitter.initialized ) {
|
||||
blitter.getPipeline().update( blitter );
|
||||
size_t eyes = metadata["eyes"].as<size_t>();
|
||||
for ( size_t eye = 0; eye < eyes; ++eye ) {
|
||||
blitter.descriptor.subpass = 2 * eye + 1;
|
||||
if ( !blitter.hasPipeline( blitter.descriptor ) ) continue;
|
||||
blitter.getPipeline( blitter.descriptor ).update( blitter, blitter.descriptor );
|
||||
}
|
||||
blitter.descriptor.subpass = 1;
|
||||
// blitter.getPipeline().update( blitter );
|
||||
// blitter.updatePipelines();
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
|
||||
this->setTarget( this->getName() );
|
||||
std::string type = metadata["type"].as<std::string>();
|
||||
size_t subpasses = metadata["subpasses"].as<size_t>();
|
||||
size_t msaa = metadata["samples"].is<size_t>() ? ext::vulkan::sampleCount(metadata["samples"].as<size_t>()) : ext::vulkan::settings::msaa;
|
||||
size_t msaa = metadata["samples"].is<size_t>() ? ext::vulkan::sampleCount(metadata["samples"].as<size_t>()) : ext::vulkan::settings::msaa;
|
||||
if ( subpasses == 0 ) subpasses = 1;
|
||||
renderTarget.device = &device;
|
||||
for ( size_t currentPass = 0; currentPass < subpasses; ++currentPass ) {
|
||||
@ -338,9 +338,10 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
|
||||
// do not attach if we're requesting no blitter shaders
|
||||
blitter.process = false;
|
||||
} else {
|
||||
std::string fragmentShaderFilename = ( msaa <= 1 ) ? "no-msaa." : "";
|
||||
blitter.material.initializeShaders({
|
||||
{uf::io::root+"/shaders/display/renderTarget.vert.spv", ext::vulkan::enums::Shader::VERTEX},
|
||||
{uf::io::root+"/shaders/display/renderTarget.frag.spv", ext::vulkan::enums::Shader::FRAGMENT}
|
||||
{uf::io::root+"/shaders/display/renderTarget.vert.spv", VK_SHADER_STAGE_VERTEX_BIT},
|
||||
{uf::io::root+"/shaders/display/renderTarget." + fragmentShaderFilename + "frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT}
|
||||
});
|
||||
}
|
||||
if ( metadata["type"].as<std::string>() == "vxgi" ) {
|
||||
@ -420,6 +421,18 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
|
||||
texture.aliasAttachment(attachment);
|
||||
}
|
||||
}
|
||||
/*
|
||||
if ( blitter.process ) {
|
||||
auto& mainRenderMode = ext::vulkan::getRenderMode("");
|
||||
size_t eyes = mainRenderMode.metadata["eyes"].as<size_t>();
|
||||
for ( size_t eye = 0; eye < eyes; ++eye ) {
|
||||
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
|
||||
descriptor.subpass = 2 * eye + 1;
|
||||
if ( blitter.hasPipeline( descriptor ) ) continue;
|
||||
blitter.initializePipeline( descriptor );
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,7 +441,9 @@ void ext::vulkan::RenderTargetRenderMode::tick() {
|
||||
if ( metadata["type"].as<std::string>() == "vxgi" ) {
|
||||
if ( ext::vulkan::states::resized ) {
|
||||
renderTarget.initialize( *renderTarget.device );
|
||||
if ( blitter.process ) blitter.getPipeline().update( blitter );
|
||||
if ( blitter.process ) {
|
||||
blitter.getPipeline().update( blitter );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -445,7 +460,21 @@ void ext::vulkan::RenderTargetRenderMode::tick() {
|
||||
texture.sampler.descriptor.filter.mag = filter;
|
||||
texture.aliasAttachment(attachment);
|
||||
}
|
||||
if ( blitter.process ) blitter.getPipeline().update( blitter );
|
||||
if ( blitter.process ) {
|
||||
auto& mainRenderMode = ext::vulkan::getRenderMode("");
|
||||
size_t eyes = mainRenderMode.metadata["eyes"].as<size_t>();
|
||||
for ( size_t eye = 0; eye < eyes; ++eye ) {
|
||||
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
|
||||
descriptor.subpass = 2 * eye + 1;
|
||||
if ( !blitter.hasPipeline( descriptor ) ) {
|
||||
// continue;
|
||||
blitter.initializePipeline( descriptor );
|
||||
} else {
|
||||
blitter.getPipeline( descriptor ).update( blitter, descriptor );
|
||||
}
|
||||
}
|
||||
// blitter.getPipeline().update( blitter );
|
||||
}
|
||||
}
|
||||
}
|
||||
void ext::vulkan::RenderTargetRenderMode::destroy() {
|
||||
|
||||
@ -197,12 +197,13 @@ void ext::vulkan::RenderTarget::initialize( Device& device ) {
|
||||
}
|
||||
}
|
||||
descriptions.push_back(description);
|
||||
/*
|
||||
std::cout << "Pass: " << descriptions.size() - 1 << std::endl;
|
||||
for ( auto& color : pass.colors ) std::cout << "Color: " << color.attachment << "\t" << std::hex << color.layout << "\t" << this->attachments[color.attachment].image << std::endl;
|
||||
for ( auto& input : pass.inputs ) std::cout << "Input: " << input.attachment << "\t" << std::hex << input.layout << "\t" << this->attachments[input.attachment].image << std::endl;
|
||||
|
||||
#if 0
|
||||
UF_DEBUG_MSG("Pass: " << descriptions.size() - 1);
|
||||
for ( auto& color : pass.colors ) UF_DEBUG_MSG("Color: " << color.attachment << "\t" << std::hex << color.layout << "\t" << this->attachments[color.attachment].image);
|
||||
for ( auto& input : pass.inputs ) UF_DEBUG_MSG("Input: " << input.attachment << "\t" << std::hex << input.layout << "\t" << this->attachments[input.attachment].image);
|
||||
std::cout << std::endl;
|
||||
*/
|
||||
#endif
|
||||
// transition dependency between subpasses
|
||||
dependency.srcSubpass = dependency.dstSubpass;
|
||||
dependency.srcStageMask = dependency.dstStageMask;
|
||||
@ -253,15 +254,15 @@ void ext::vulkan::RenderTarget::initialize( Device& device ) {
|
||||
|
||||
dependencies.push_back(dependency);
|
||||
}
|
||||
/*
|
||||
|
||||
#if 0
|
||||
for ( auto& dependency : dependencies ) {
|
||||
std::cout << "Pass: " << dependency.srcSubpass << " -> " << dependency.dstSubpass << std::endl;
|
||||
std::cout << "\tStage: " << std::hex << dependency.srcStageMask << " -> " << std::hex << dependency.dstStageMask << std::endl;
|
||||
std::cout << "\tAccess: " << std::hex << dependency.srcAccessMask << " -> " << std::hex << dependency.dstAccessMask << std::endl;
|
||||
UF_DEBUG_MSG("Pass: " << dependency.srcSubpass << " -> " << dependency.dstSubpass);
|
||||
UF_DEBUG_MSG("\tStage: " << std::hex << dependency.srcStageMask << " -> " << std::hex << dependency.dstStageMask);
|
||||
UF_DEBUG_MSG("\tAccess: " << std::hex << dependency.srcAccessMask << " -> " << std::hex << dependency.dstAccessMask);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
*/
|
||||
|
||||
#endif
|
||||
VkRenderPassCreateInfo renderPassInfo = {};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.flags = 0;
|
||||
|
||||
787
engine/src/ext/vulkan/shader.cpp
Normal file
787
engine/src/ext/vulkan/shader.cpp
Normal file
@ -0,0 +1,787 @@
|
||||
#if UF_USE_VULKAN
|
||||
|
||||
#include <uf/ext/vulkan/graphic.h>
|
||||
#include <uf/ext/vulkan/shader.h>
|
||||
#include <uf/ext/vulkan/initializers.h>
|
||||
#include <uf/ext/vulkan/vulkan.h>
|
||||
#include <uf/ext/openvr/openvr.h>
|
||||
#include <uf/utils/camera/camera.h>
|
||||
|
||||
#include <spirv_cross/spirv_cross.hpp>
|
||||
#include <spirv_cross/spirv_glsl.hpp>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
#define VK_DEBUG_VALIDATION_MESSAGE(x)\
|
||||
//VK_VALIDATION_MESSAGE(x);
|
||||
|
||||
ext::json::Value ext::vulkan::definitionToJson(/*const*/ ext::json::Value& definition ) {
|
||||
ext::json::Value member;
|
||||
// is object
|
||||
if ( !ext::json::isNull(definition["members"]) ) {
|
||||
ext::json::forEach(definition["members"], [&](/*const*/ ext::json::Value& value){
|
||||
std::string key = uf::string::split(value["name"].as<std::string>(), " ").back();
|
||||
member[key] = ext::vulkan::definitionToJson(value);
|
||||
});
|
||||
// is primitive
|
||||
} else if ( !ext::json::isNull(definition["value"]) ) {
|
||||
// is array of structs
|
||||
if ( definition["struct"].as<bool>() ) {
|
||||
ext::json::forEach(definition["value"], [&](/*const*/ ext::json::Value& value){
|
||||
ext::json::Value parsed;
|
||||
parsed["name"] = definition["name"];
|
||||
parsed["size"] = definition["size"];
|
||||
parsed["struct"] = definition["struct"];
|
||||
parsed["members"] = value;
|
||||
parsed = ext::vulkan::definitionToJson(parsed);
|
||||
member.emplace_back(parsed);
|
||||
});
|
||||
} else {
|
||||
member = definition["value"];
|
||||
}
|
||||
}
|
||||
return member;
|
||||
}
|
||||
ext::vulkan::userdata_t ext::vulkan::jsonToUserdata( const ext::json::Value& payload, const ext::json::Value& definition ) {
|
||||
size_t bufferLen = definition["size"].as<size_t>();
|
||||
|
||||
ext::vulkan::userdata_t userdata;
|
||||
userdata.create(bufferLen);
|
||||
|
||||
uint8_t* byteBuffer = (uint8_t*) (void*) userdata;
|
||||
uint8_t* byteBufferStart = byteBuffer;
|
||||
uint8_t* byteBufferEnd = byteBuffer + bufferLen;
|
||||
|
||||
#if UF_JSON_NLOHMANN_ORDERED
|
||||
// JSON is ordered, we can just push directly
|
||||
#define UF_SHADER_TRACK_NAMES 0
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
std::vector<std::string> variableName;
|
||||
#endif
|
||||
std::function<void(const ext::json::Value&)> parse = [&]( const ext::json::Value& value ){
|
||||
// is array or object
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( ext::json::isObject(value) ) {
|
||||
ext::json::forEach(value, [&]( const std::string& name, const ext::json::Value& member ){
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
variableName.emplace_back(name);
|
||||
#endif
|
||||
parse(member);
|
||||
});
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if ( ext::json::isArray(value) ) {
|
||||
ext::json::forEach(value, [&]( size_t i, const ext::json::Value& element ){
|
||||
variableName.emplace_back("["+std::to_string(i)+"]");
|
||||
parse(element);
|
||||
});
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if ( ext::json::isArray(value) || ext::json::isObject(value) ) {
|
||||
ext::json::forEach(value, parse);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
std::string path = uf::string::join(variableName, ".");
|
||||
path = uf::string::replace( path, ".[", "[" );
|
||||
VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = " << value.dump());
|
||||
#endif
|
||||
// is strictly an int
|
||||
if ( value.is<int>(true) ) {
|
||||
size_t size = sizeof(int32_t);
|
||||
auto get = value.as<int32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
// is strictly an unsigned int
|
||||
} else if ( value.is<size_t>(true) ) {
|
||||
size_t size = sizeof(uint32_t);
|
||||
auto get = value.as<uint32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
// is strictly a float
|
||||
} else if ( value.is<float>(true) ) {
|
||||
size_t size = sizeof(float);
|
||||
auto get = value.as<float>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
}
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
};
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Updating " << name << " in " << filename);
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
parse(payload);
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
#else
|
||||
auto pushValue = [&]( const std::string& primitive, const ext::json::Value& input ){
|
||||
if ( primitive == "bool" ) {
|
||||
size_t size = sizeof(bool); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<bool>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int8_t" ) {
|
||||
size_t size = sizeof(int8_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
|
||||
// auto get = input.as<int8_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "uint8_t" ) {
|
||||
size_t size = sizeof(uint8_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<uint8_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int16_t" ) {
|
||||
size_t size = sizeof(int16_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<int16_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "uint16_t" ) {
|
||||
size_t size = sizeof(uint16_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<uint16_t>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int32_t" ) {
|
||||
size_t size = sizeof(int32_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<int32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
}
|
||||
else if ( primitive == "uint32_t" ) {
|
||||
size_t size = sizeof(uint32_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<int32_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "int64_t" ) {
|
||||
size_t size = sizeof(int64_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<uint64_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "uint64_t" ) {
|
||||
size_t size = sizeof(uint64_t); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<uint64_t>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "half" ) {
|
||||
size_t size = sizeof(float); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
// auto get = input.as<float>();
|
||||
// memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "float" ) {
|
||||
size_t size = sizeof(float); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
auto get = input.as<float>();
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
} else if ( primitive == "double" ) {
|
||||
auto get = input.as<double>();
|
||||
size_t size = sizeof(double); // v["size"].as<size_t>();
|
||||
if ( byteBufferEnd < byteBuffer + size ) return false; // overflow
|
||||
memcpy( byteBuffer, &get, size );
|
||||
byteBuffer += size;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#define UF_SHADER_TRACK_NAMES 0
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
bool SKIP_ADD = false;
|
||||
std::vector<std::string> variableName;
|
||||
#endif
|
||||
std::function<void(const ext::json::Value&, const ext::json::Value&)> parseDefinition = [&](const ext::json::Value& input, const ext::json::Value& definition ){
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( SKIP_ADD ) {
|
||||
SKIP_ADD = false;
|
||||
} else {
|
||||
auto split = uf::string::split(definition["name"].as<std::string>(), " ");
|
||||
std::string type = split.front();
|
||||
std::string name = split.back();
|
||||
variableName.emplace_back(name);
|
||||
}
|
||||
#endif
|
||||
// is object
|
||||
if ( !ext::json::isNull(definition["members"]) ) {
|
||||
ext::json::forEach(definition["members"], [&](const ext::json::Value& member){
|
||||
std::string key = uf::string::split(member["name"].as<std::string>(), " ").back();
|
||||
parseDefinition(input[key], member);
|
||||
});
|
||||
// is array or primitive
|
||||
} else if ( !ext::json::isNull(definition["value"]) ) {
|
||||
// is object
|
||||
auto split = uf::string::split(definition["name"].as<std::string>(), " ");
|
||||
std::string type = split.front();
|
||||
std::string name = split.back();
|
||||
std::regex regex("^(?:(.+?)\\<)?(.+?)(?:\\>)?(?:\\[(\\d+)\\])?$");
|
||||
std::smatch match;
|
||||
if ( !std::regex_search( type, match, regex ) ) {
|
||||
std::cout << "Ill formatted typename: " << definition["name"].as<std::string>() << std::endl;
|
||||
return;
|
||||
}
|
||||
std::string vectorMatrix = match[1].str();
|
||||
std::string primitive = match[2].str();
|
||||
std::string arraySize = match[3].str();
|
||||
if ( ext::json::isObject(input) ) {
|
||||
ext::json::Value cloned;
|
||||
cloned["name"] = definition["name"];
|
||||
cloned["size"] = definition["size"].as<size_t>() / definition["value"].size();
|
||||
cloned["members"] = definition["value"][0];
|
||||
parseDefinition( input, cloned );
|
||||
}
|
||||
// is array
|
||||
else if ( ext::json::isArray(input) ) {
|
||||
ext::json::forEach( input, [&]( size_t i, const ext::json::Value& value){
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
variableName.emplace_back("["+std::to_string(i)+"]");
|
||||
SKIP_ADD = true;
|
||||
#endif
|
||||
parseDefinition(input[i], definition);
|
||||
});
|
||||
}
|
||||
// is primitive
|
||||
else {
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
std::string path = uf::string::join(variableName, ".");
|
||||
path = uf::string::replace( path, ".[", "[" );
|
||||
VK_VALIDATION_MESSAGE("[" << (byteBuffer - byteBufferStart) << " / "<< (byteBufferEnd - byteBuffer) <<"]\tInserting: " << path << " = (" << primitive << ") " << input.dump());
|
||||
#endif
|
||||
pushValue( primitive, input );
|
||||
}
|
||||
}
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
if ( !variableName.empty() ) variableName.pop_back();
|
||||
#endif
|
||||
};
|
||||
auto& definitions = metadata["definitions"]["uniforms"][name];
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Updating " << name << " in " << filename);
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
parseDefinition(payload, definitions);
|
||||
#if UF_SHADER_TRACK_NAMES
|
||||
VK_VALIDATION_MESSAGE("Iterator: " << (void*) byteBuffer << "\t" << (void*) byteBufferEnd << "\t" << (byteBufferEnd - byteBuffer));
|
||||
#endif
|
||||
#endif
|
||||
return userdata;
|
||||
}
|
||||
|
||||
void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const std::string& filename, VkShaderStageFlagBits stage ) {
|
||||
this->device = &device;
|
||||
ext::vulkan::Buffers::initialize( device );
|
||||
|
||||
std::string spirv;
|
||||
|
||||
{
|
||||
std::ifstream is(this->filename = filename, std::ios::binary | std::ios::in | std::ios::ate);
|
||||
if ( !is.is_open() ) {
|
||||
VK_VALIDATION_MESSAGE("Error: Could not open shader file \"" << filename << "\"");
|
||||
return;
|
||||
}
|
||||
is.seekg(0, std::ios::end); spirv.reserve(is.tellg()); is.seekg(0, std::ios::beg);
|
||||
spirv.assign((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
|
||||
|
||||
assert(spirv.size() > 0);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
VkShaderModuleCreateInfo moduleCreateInfo = {};
|
||||
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
moduleCreateInfo.codeSize = spirv.size();
|
||||
moduleCreateInfo.pCode = (uint32_t*) spirv.data();
|
||||
|
||||
VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &module));
|
||||
}
|
||||
|
||||
{
|
||||
descriptor.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
descriptor.stage = stage;
|
||||
descriptor.module = module;
|
||||
descriptor.pName = "main";
|
||||
|
||||
assert(descriptor.module != VK_NULL_HANDLE);
|
||||
}
|
||||
// set up metadata
|
||||
{
|
||||
metadata["filename"] = filename;
|
||||
metadata["type"] = "";
|
||||
}
|
||||
// do reflection
|
||||
{
|
||||
spirv_cross::Compiler comp( (uint32_t*) &spirv[0], spirv.size() / 4 );
|
||||
spirv_cross::ShaderResources res = comp.get_shader_resources();
|
||||
|
||||
std::function<ext::json::Value(spirv_cross::TypeID)> parseMembers = [&]( spirv_cross::TypeID type_id ) {
|
||||
auto parseMember = [&]( auto type_id ){
|
||||
uf::Serializer payload;
|
||||
|
||||
auto type = comp.get_type(type_id);
|
||||
|
||||
std::string name = "";
|
||||
size_t size = 1;
|
||||
ext::json::Value value;
|
||||
switch ( type.basetype ) {
|
||||
case spirv_cross::SPIRType::BaseType::Boolean: name = "bool"; size = sizeof(bool); value = (bool) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::SByte: name = "int8_t"; size = sizeof(int8_t); value = (int8_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UByte: name = "uint8_t"; size = sizeof(uint8_t); value = (uint8_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Short: name = "int16_t"; size = sizeof(int16_t); value = (int16_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UShort: name = "uint16_t"; size = sizeof(uint16_t); value = (uint16_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Int: name = "int32_t"; size = sizeof(int32_t); value = (int32_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UInt: name = "uint32_t"; size = sizeof(uint32_t); value = (uint32_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Int64: name = "int64_t"; size = sizeof(int64_t); value = (int64_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::UInt64: name = "uint64_t"; size = sizeof(uint64_t); value = (uint64_t) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Half: name = "half"; size = sizeof(float)/2; value = (float) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Float: name = "float"; size = sizeof(float); value = (float) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Double: name = "double"; size = sizeof(double); value = (double) 0; break;
|
||||
case spirv_cross::SPIRType::BaseType::Image: name = "image2D"; break;
|
||||
case spirv_cross::SPIRType::BaseType::SampledImage: name = "sampler2D"; break;
|
||||
case spirv_cross::SPIRType::BaseType::Sampler: name = "sampler"; break;
|
||||
default:
|
||||
name = comp.get_name(type_id);
|
||||
size = comp.get_declared_struct_size(type);
|
||||
break;
|
||||
}
|
||||
if ( name == "" ) name = comp.get_name(type.type_alias);
|
||||
if ( name == "" ) name = comp.get_name(type.type_alias);
|
||||
if ( name == "" ) name = comp.get_name(type.parent_type);
|
||||
if ( name == "" ) name = comp.get_fallback_name(type_id);
|
||||
if ( type.vecsize > 1 ) {
|
||||
name = "<"+name+">";
|
||||
if ( type.columns > 1 ) {
|
||||
name = "Matrix"+std::to_string(type.vecsize)+"x"+std::to_string(type.columns)+name;
|
||||
} else {
|
||||
name = "Vector"+std::to_string(type.vecsize)+name;
|
||||
}
|
||||
}
|
||||
{
|
||||
ext::json::Value source = value;
|
||||
value = ext::json::array();
|
||||
for ( size_t i = 0; i < type.vecsize * type.columns; ++i ) {
|
||||
value.emplace_back(source);
|
||||
}
|
||||
size *= type.columns * type.vecsize;
|
||||
}
|
||||
for ( auto arraySize : type.array ) {
|
||||
if ( arraySize > 1 ) {
|
||||
ext::json::Value source = value;
|
||||
value = ext::json::array();
|
||||
for ( size_t i = 0; i < arraySize; ++i ) {
|
||||
value.emplace_back(source);
|
||||
}
|
||||
name += "[" + std::to_string(arraySize) + "]";
|
||||
size *= arraySize;
|
||||
}
|
||||
}
|
||||
if ( ext::json::isArray(value) && value.size() == 1 ) {
|
||||
value = value[0];
|
||||
}
|
||||
payload["name"] = name;
|
||||
payload["size"] = size;
|
||||
payload["value"] = value;
|
||||
return payload;
|
||||
};
|
||||
uf::Serializer payload = ext::json::array();
|
||||
const auto& type = comp.get_type(type_id);
|
||||
for ( auto& member_type_id : type.member_types ) {
|
||||
const auto& member_type = comp.get_type(member_type_id);
|
||||
std::string name = comp.get_member_name(type.type_alias, payload.size());
|
||||
if ( name == "" ) name = comp.get_member_name(type.parent_type, payload.size());
|
||||
if ( name == "" ) name = comp.get_member_name(type_id, payload.size());
|
||||
|
||||
auto& entry = payload.emplace_back();
|
||||
auto parsed = parseMember(member_type_id);
|
||||
std::string type_name = parsed["name"];
|
||||
entry["name"] = type_name + " " + name;
|
||||
if ( member_type.basetype == spirv_cross::SPIRType::BaseType::Struct ) {
|
||||
entry["struct"] = true;
|
||||
auto parsed = parseMembers(member_type_id);
|
||||
if ( !member_type.array.empty() && member_type.array[0] > 1 ) {
|
||||
size_t size = comp.get_declared_struct_size(member_type);
|
||||
for ( auto arraySize : member_type.array ) {
|
||||
ext::json::Value source = parsed;
|
||||
parsed = ext::json::array();
|
||||
for ( size_t i = 0; i < arraySize; ++i ) {
|
||||
parsed.emplace_back(source);
|
||||
}
|
||||
size = size * arraySize;
|
||||
}
|
||||
entry["size"] = size;
|
||||
entry["value"] = parsed;
|
||||
} else {
|
||||
entry["size"] = comp.get_declared_struct_size(member_type);
|
||||
entry["members"] = parsed;
|
||||
}
|
||||
} else {
|
||||
entry["size"] = parsed["size"];
|
||||
entry["value"] = parsed["value"];
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
};
|
||||
|
||||
auto parseResource = [&]( const spirv_cross::Resource& resource, VkDescriptorType descriptorType, size_t index ) {
|
||||
const auto& type = comp.get_type(resource.type_id);
|
||||
const auto& base_type = comp.get_type(resource.base_type_id);
|
||||
std::string name = resource.name;
|
||||
size_t arraySize = 1;
|
||||
if ( !type.array.empty() ) {
|
||||
arraySize = type.array[0];
|
||||
}
|
||||
|
||||
switch ( descriptorType ) {
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {
|
||||
std::string tname = "";
|
||||
switch ( type.image.dim ) {
|
||||
case spv::Dim::Dim1D: tname = "1D"; break;
|
||||
case spv::Dim::Dim2D: tname = "2D"; break;
|
||||
case spv::Dim::Dim3D: tname = "3D"; break;
|
||||
case spv::Dim::DimCube: tname = "Cube"; break;
|
||||
case spv::Dim::DimRect: tname = "Rect"; break;
|
||||
case spv::Dim::DimBuffer: tname = "Buffer"; break;
|
||||
case spv::Dim::DimSubpassData: tname = "SubpassData"; break;
|
||||
|
||||
}
|
||||
size_t binding = comp.get_decoration(resource.id, spv::DecorationBinding);
|
||||
std::string key = std::to_string(binding);
|
||||
metadata["definitions"]["textures"][key]["name"] = name;
|
||||
metadata["definitions"]["textures"][key]["index"] = index;
|
||||
metadata["definitions"]["textures"][key]["binding"] = binding;
|
||||
metadata["definitions"]["textures"][key]["size"] = arraySize;
|
||||
metadata["definitions"]["textures"][key]["type"] = tname;
|
||||
} break;
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: {
|
||||
size_t bufferSize = comp.get_declared_struct_size(base_type);
|
||||
if ( bufferSize <= 0 ) break;
|
||||
if ( bufferSize > device.properties.limits.maxUniformBufferRange ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer length of " << bufferSize << " for shader " << filename);
|
||||
bufferSize = device.properties.limits.maxUniformBufferRange;
|
||||
}
|
||||
size_t misalignment = bufferSize % device.properties.limits.minStorageBufferOffsetAlignment;
|
||||
if ( misalignment != 0 ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer alignment of " << misalignment << " for shader " << filename << ", correcting...");
|
||||
bufferSize += misalignment;
|
||||
}
|
||||
{
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Uniform size of " << bufferSize << " for shader " << filename);
|
||||
auto& uniform = uniforms.emplace_back();
|
||||
uniform.create( bufferSize );
|
||||
}
|
||||
// generate definition to JSON
|
||||
{
|
||||
metadata["definitions"]["uniforms"][name]["name"] = name;
|
||||
metadata["definitions"]["uniforms"][name]["index"] = index;
|
||||
metadata["definitions"]["uniforms"][name]["size"] = bufferSize;
|
||||
metadata["definitions"]["uniforms"][name]["members"] = parseMembers(resource.type_id);
|
||||
}
|
||||
} break;
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {
|
||||
// generate definition to JSON
|
||||
{
|
||||
metadata["definitions"]["storage"][name]["name"] = name;
|
||||
metadata["definitions"]["storage"][name]["index"] = index;
|
||||
metadata["definitions"]["storage"][name]["members"] = parseMembers(resource.type_id);
|
||||
}
|
||||
// test
|
||||
{
|
||||
// updateUniform(name, getUniform(name));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
descriptorSetLayoutBindings.push_back( ext::vulkan::initializers::descriptorSetLayoutBinding( descriptorType, stage, comp.get_decoration(resource.id, spv::DecorationBinding), arraySize ) );
|
||||
};
|
||||
|
||||
|
||||
//for ( const auto& resource : res.key ) {
|
||||
#define LOOP_RESOURCES( key, type ) for ( size_t i = 0; i < res.key.size(); ++i ) {\
|
||||
const auto& resource = res.key[i];\
|
||||
VK_DEBUG_VALIDATION_MESSAGE("["<<filename<<"] Found resource: "#type " with binding: " << comp.get_decoration(resource.id, spv::DecorationBinding));\
|
||||
parseResource( resource, type, i );\
|
||||
}
|
||||
LOOP_RESOURCES( sampled_images, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER );
|
||||
LOOP_RESOURCES( separate_images, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE );
|
||||
LOOP_RESOURCES( storage_images, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE );
|
||||
LOOP_RESOURCES( separate_samplers, VK_DESCRIPTOR_TYPE_SAMPLER );
|
||||
LOOP_RESOURCES( subpass_inputs, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT );
|
||||
LOOP_RESOURCES( uniform_buffers, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER );
|
||||
LOOP_RESOURCES( storage_buffers, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER );
|
||||
#undef LOOP_RESOURCES
|
||||
|
||||
for ( const auto& resource : res.push_constant_buffers ) {
|
||||
const auto& type = comp.get_type(resource.type_id);
|
||||
const auto& base_type = comp.get_type(resource.base_type_id);
|
||||
std::string name = resource.name;
|
||||
size_t size = comp.get_declared_struct_size(type);
|
||||
if ( size <= 0 ) continue;
|
||||
// not a multiple of 4, for some reason
|
||||
if ( size % 4 != 0 ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of " << size << " for shader " << filename << ", must be multiple of 4, correcting...");
|
||||
size /= 4;
|
||||
++size;
|
||||
size *= 4;
|
||||
}
|
||||
if ( size > device.properties.limits.maxPushConstantsSize ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of " << size << " for shader " << filename);
|
||||
size = device.properties.limits.maxPushConstantsSize;
|
||||
}
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Push constant size of " << size << " for shader " << filename);
|
||||
{
|
||||
auto& pushConstant = pushConstants.emplace_back();
|
||||
pushConstant.create( size );
|
||||
}
|
||||
// generate definition to JSON
|
||||
{
|
||||
metadata["definitions"]["pushConstants"][name]["name"] = name;
|
||||
metadata["definitions"]["pushConstants"][name]["size"] = size;
|
||||
metadata["definitions"]["pushConstants"][name]["index"] = pushConstants.size() - 1;
|
||||
metadata["definitions"]["pushConstants"][name]["members"] = parseMembers(resource.type_id);
|
||||
}
|
||||
}
|
||||
|
||||
size_t specializationSize = 0;
|
||||
for ( const auto& constant : comp.get_specialization_constants() ) {
|
||||
const auto& value = comp.get_constant(constant.id);
|
||||
const auto& type = comp.get_type(value.constant_type);
|
||||
size_t size = 4; //comp.get_declared_struct_size(type);
|
||||
|
||||
VkSpecializationMapEntry specializationMapEntry;
|
||||
specializationMapEntry.constantID = constant.constant_id;
|
||||
specializationMapEntry.size = size;
|
||||
specializationMapEntry.offset = specializationSize;
|
||||
specializationMapEntries.push_back(specializationMapEntry);
|
||||
specializationSize += size;
|
||||
}
|
||||
if ( specializationSize > 0 ) {
|
||||
specializationConstants.create( specializationSize );
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Specialization constants size of " << specializationSize << " for shader " << filename);
|
||||
|
||||
uint8_t* s = (uint8_t*) (void*) specializationConstants;
|
||||
size_t offset = 0;
|
||||
|
||||
metadata["specializationConstants"] = ext::json::array();
|
||||
for ( const auto& constant : comp.get_specialization_constants() ) {
|
||||
const auto& value = comp.get_constant(constant.id);
|
||||
const auto& type = comp.get_type(value.constant_type);
|
||||
std::string name = comp.get_name (constant.id);
|
||||
|
||||
ext::json::Value member;
|
||||
|
||||
size_t size = 4;
|
||||
uint8_t buffer[size];
|
||||
switch ( type.basetype ) {
|
||||
case spirv_cross::SPIRType::UInt: {
|
||||
auto v = value.scalar();
|
||||
member["type"] = "uint32_t";
|
||||
member["value"] = v;
|
||||
member["validate"] = true;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
case spirv_cross::SPIRType::Int: {
|
||||
auto v = value.scalar_i32();
|
||||
member["type"] = "int32_t";
|
||||
member["value"] = v;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
case spirv_cross::SPIRType::Float: {
|
||||
auto v = value.scalar_f32();
|
||||
member["type"] = "float";
|
||||
member["value"] = v;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
case spirv_cross::SPIRType::Boolean: {
|
||||
auto v = value.scalar()!=0;
|
||||
member["type"] = "bool";
|
||||
member["value"] = v;
|
||||
memcpy( &buffer[0], &v, sizeof(v) );
|
||||
} break;
|
||||
default: {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Unregistered specialization constant type at offset " << offset << " for shader " << filename );
|
||||
} break;
|
||||
}
|
||||
member["name"] = name;
|
||||
member["size"] = size;
|
||||
member["default"] = member["value"];
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Specialization constant: " << member["type"].as<std::string>() << " " << name << " = " << member["value"].dump() << "; at offset " << offset << " for shader " << filename );
|
||||
metadata["specializationConstants"].emplace_back(member);
|
||||
|
||||
memcpy( &s[offset], &buffer, size );
|
||||
offset += size;
|
||||
}
|
||||
/*
|
||||
{
|
||||
specializationInfo = {};
|
||||
specializationInfo.dataSize = specializationSize;
|
||||
specializationInfo.mapEntryCount = specializationMapEntries.size();
|
||||
specializationInfo.pMapEntries = specializationMapEntries.data();
|
||||
specializationInfo.pData = (void*) specializationConstants;
|
||||
descriptor.pSpecializationInfo = &specializationInfo;
|
||||
}
|
||||
*/
|
||||
}
|
||||
/*
|
||||
*/
|
||||
// LOOP_RESOURCES( stage_inputs, VkVertexInputAttributeDescription );
|
||||
// LOOP_RESOURCES( stage_outputs, VkPipelineColorBlendAttachmentState );
|
||||
}
|
||||
|
||||
// organize layouts
|
||||
{
|
||||
std::sort( descriptorSetLayoutBindings.begin(), descriptorSetLayoutBindings.end(), [&]( const VkDescriptorSetLayoutBinding& l, const VkDescriptorSetLayoutBinding& r ){
|
||||
return l.binding < r.binding;
|
||||
} );
|
||||
}
|
||||
|
||||
// update uniform buffers
|
||||
for ( auto& uniform : uniforms ) {
|
||||
auto& userdata = uniform.data();
|
||||
initializeBuffer(
|
||||
(void*) userdata.data,
|
||||
userdata.len,
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ext::vulkan::Shader::destroy() {
|
||||
if ( !device ) return;
|
||||
|
||||
ext::vulkan::Buffers::destroy();
|
||||
|
||||
if ( module != VK_NULL_HANDLE ) {
|
||||
vkDestroyShaderModule( *device, module, nullptr );
|
||||
module = VK_NULL_HANDLE;
|
||||
descriptor = {};
|
||||
}
|
||||
for ( auto& userdata : uniforms ) userdata.destroy();
|
||||
for ( auto& userdata : pushConstants ) userdata.destroy();
|
||||
uniforms.clear();
|
||||
pushConstants.clear();
|
||||
}
|
||||
|
||||
bool ext::vulkan::Shader::validate() {
|
||||
// check if uniforms match buffer size
|
||||
bool valid = true;
|
||||
{
|
||||
auto it = uniforms.begin();
|
||||
for ( auto& buffer : buffers ) {
|
||||
if ( !(buffer.usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) ) continue;
|
||||
if ( it == uniforms.end() ) break;
|
||||
auto& uniform = *(it++);
|
||||
if ( uniform.data().len != buffer.allocationInfo.size ) {
|
||||
VK_DEBUG_VALIDATION_MESSAGE("Uniform size mismatch: Expected " << buffer.allocationInfo.size << ", got " << uniform.data().len << "; fixing...");
|
||||
uniform.destroy();
|
||||
uniform.create(buffer.allocationInfo.size);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
bool ext::vulkan::Shader::hasUniform( const std::string& name ) {
|
||||
return !ext::json::isNull(metadata["definitions"]["uniforms"][name]);
|
||||
}
|
||||
ext::vulkan::Buffer* ext::vulkan::Shader::getUniformBuffer( const std::string& name ) {
|
||||
if ( !hasUniform(name) ) return NULL;
|
||||
size_t uniformIndex = metadata["definitions"]["uniforms"][name]["index"].as<size_t>();
|
||||
for ( size_t bufferIndex = 0, uniformCounter = 0; bufferIndex < buffers.size(); ++bufferIndex ) {
|
||||
if ( !(buffers[bufferIndex].usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) ) continue;
|
||||
if ( uniformCounter++ != uniformIndex ) continue;
|
||||
return &buffers[bufferIndex];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
ext::vulkan::userdata_t& ext::vulkan::Shader::getUniform( const std::string& name ) {
|
||||
if ( !hasUniform(name) ) {
|
||||
static ext::vulkan::userdata_t null;
|
||||
return null;
|
||||
}
|
||||
auto& definition = metadata["definitions"]["uniforms"][name];
|
||||
size_t uniformSize = definition["size"].as<size_t>();
|
||||
size_t uniformIndex = definition["index"].as<size_t>();
|
||||
auto& userdata = uniforms[uniformIndex];
|
||||
return userdata;
|
||||
}
|
||||
bool ext::vulkan::Shader::updateUniform( const std::string& name ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
auto& uniform = getUniform(name);
|
||||
return updateUniform(name, uniform);
|
||||
}
|
||||
bool ext::vulkan::Shader::updateUniform( const std::string& name, const ext::vulkan::userdata_t& userdata ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
auto* bufferObject = getUniformBuffer(name);
|
||||
if ( !bufferObject ) return false;
|
||||
size_t size = std::max(metadata["definitions"]["uniforms"][name]["size"].as<size_t>(), bufferObject->allocationInfo.size);
|
||||
updateBuffer( (void*) userdata, size, *bufferObject );
|
||||
return true;
|
||||
}
|
||||
|
||||
uf::Serializer ext::vulkan::Shader::getUniformJson( const std::string& name, bool cache ) {
|
||||
if ( !hasUniform(name) ) return ext::json::null();
|
||||
if ( cache && !ext::json::isNull(metadata["uniforms"][name]) ) return metadata["uniforms"][name];
|
||||
auto& definition = metadata["definitions"]["uniforms"][name];
|
||||
if ( cache ) return metadata["uniforms"][name] = definitionToJson(definition);
|
||||
return definitionToJson(definition);
|
||||
}
|
||||
ext::vulkan::userdata_t ext::vulkan::Shader::getUniformUserdata( const std::string& name, const ext::json::Value& payload ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
return jsonToUserdata(payload, metadata["definitions"]["uniforms"][name]);
|
||||
}
|
||||
bool ext::vulkan::Shader::updateUniform( const std::string& name, const ext::json::Value& payload ) {
|
||||
if ( !hasUniform(name) ) return false;
|
||||
|
||||
auto* bufferObject = getUniformBuffer(name);
|
||||
if ( !bufferObject ) return false;
|
||||
|
||||
auto uniform = getUniformUserdata( name, payload );
|
||||
updateBuffer( (void*) uniform, uniform.data().len, *bufferObject );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ext::vulkan::Shader::hasStorage( const std::string& name ) {
|
||||
return !ext::json::isNull(metadata["definitions"]["storage"][name]);
|
||||
}
|
||||
|
||||
ext::vulkan::Buffer* ext::vulkan::Shader::getStorageBuffer( const std::string& name ) {
|
||||
if ( !hasStorage(name) ) return NULL;
|
||||
size_t storageIndex = metadata["definitions"]["storage"][name]["index"].as<size_t>();
|
||||
for ( size_t bufferIndex = 0, storageCounter = 0; bufferIndex < buffers.size(); ++bufferIndex ) {
|
||||
if ( !(buffers[bufferIndex].usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) ) continue;
|
||||
if ( storageCounter++ != storageIndex ) continue;
|
||||
return &buffers[bufferIndex];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
uf::Serializer ext::vulkan::Shader::getStorageJson( const std::string& name, bool cache ) {
|
||||
if ( !hasStorage(name) ) return ext::json::null();
|
||||
if ( cache && !ext::json::isNull(metadata["storage"][name]) ) return metadata["storage"][name];
|
||||
auto& definition = metadata["definitions"]["storage"][name];
|
||||
if ( cache ) return metadata["storage"][name] = definitionToJson(definition);
|
||||
return definitionToJson(definition);
|
||||
}
|
||||
ext::vulkan::userdata_t ext::vulkan::Shader::getStorageUserdata( const std::string& name, const ext::json::Value& payload ) {
|
||||
if ( !hasStorage(name) ) return false;
|
||||
return jsonToUserdata(payload, metadata["definitions"]["storage"][name]);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -9,6 +9,32 @@ ext::vulkan::Texture2D ext::vulkan::Texture2D::empty;
|
||||
ext::vulkan::Texture3D ext::vulkan::Texture3D::empty;
|
||||
ext::vulkan::TextureCube ext::vulkan::TextureCube::empty;
|
||||
|
||||
std::vector<ext::vulkan::Sampler> ext::vulkan::Sampler::samplers;
|
||||
|
||||
ext::vulkan::Sampler ext::vulkan::Sampler::retrieve( const ext::vulkan::Sampler::Descriptor& info ) {
|
||||
ext::vulkan::Sampler sampler;
|
||||
for ( auto& s : samplers ) {
|
||||
if ( memcmp( &info, &s.descriptor, sizeof(info) - sizeof(VkDescriptorImageInfo) ) != 0 ) continue;
|
||||
sampler = s;
|
||||
goto RETRIEVE_RETURN;
|
||||
/*
|
||||
auto& d = s.descriptor;
|
||||
if ( d.filter.min != info.filter.min || d.filter.mag != info.filter.mag || d.filter.borderColor != info.filter.borderColor ) continue;
|
||||
if ( d.addressMode.unnormalizedCoordinates != info.addressMode.unnormalizedCoordinates || d.addressMode.u != info.addressMode.u || d.addressMode.v != info.addressMode.v || d.addressMode.w != info.addressMode.w ) continue;
|
||||
if ( d.mip.compareEnable != info.mip.compareEnable || d.mip.compareOp != info.mip.compareOp || d.mip.mode != info.mip.mode || d.mip.lodBias != info.mip.lodBias || d.mip.min != info.mip.min || d.mip.max != info.mip.max ) continue;
|
||||
*/
|
||||
}
|
||||
{
|
||||
auto& s = samplers.emplace_back();
|
||||
s.descriptor = info;
|
||||
s.initialize( ext::vulkan::device );
|
||||
sampler = s;
|
||||
}
|
||||
RETRIEVE_RETURN:
|
||||
sampler.device = NULL;
|
||||
return sampler;
|
||||
}
|
||||
|
||||
void ext::vulkan::Sampler::initialize( Device& device ) {
|
||||
this->device = &device;
|
||||
if ( device.enabledFeatures.samplerAnisotropy == VK_FALSE ) {
|
||||
@ -41,6 +67,8 @@ void ext::vulkan::Sampler::initialize( Device& device ) {
|
||||
}
|
||||
}
|
||||
void ext::vulkan::Sampler::destroy() {
|
||||
if ( !device ) return;
|
||||
|
||||
if ( sampler != VK_NULL_HANDLE ) {
|
||||
vkDestroySampler(device->logicalDevice, sampler, nullptr);
|
||||
sampler = VK_NULL_HANDLE;
|
||||
@ -112,7 +140,7 @@ void ext::vulkan::Texture::destroy() {
|
||||
// vkFreeMemory(device->logicalDevice, deviceMemory, nullptr);
|
||||
deviceMemory = VK_NULL_HANDLE;
|
||||
}
|
||||
sampler.destroy(); // should be above
|
||||
if ( sampler.device ) sampler.destroy();
|
||||
}
|
||||
void ext::vulkan::Texture::setImageLayout(
|
||||
VkCommandBuffer cmdbuffer,
|
||||
@ -417,7 +445,8 @@ void ext::vulkan::Texture::fromBuffers(
|
||||
// Create sampler
|
||||
sampler.descriptor.mip.min = 0;
|
||||
sampler.descriptor.mip.max = static_cast<float>(this->mips);
|
||||
sampler.initialize( device );
|
||||
// sampler.initialize( device );
|
||||
sampler = ext::vulkan::Sampler::retrieve( sampler.descriptor );
|
||||
|
||||
// Create image view
|
||||
VkImageViewCreateInfo viewCreateInfo = {};
|
||||
@ -511,7 +540,8 @@ void ext::vulkan::Texture::asRenderTarget( Device& device, uint32_t width, uint3
|
||||
device.flushCommandBuffer(layoutCmd, true);
|
||||
|
||||
// Create sampler
|
||||
sampler.initialize( device );
|
||||
// sampler.initialize( device );
|
||||
sampler = ext::vulkan::Sampler::retrieve( sampler.descriptor );
|
||||
|
||||
// Create image view
|
||||
VkImageViewCreateInfo viewCreateInfo = ext::vulkan::initializers::imageViewCreateInfo();
|
||||
@ -547,7 +577,10 @@ void ext::vulkan::Texture::aliasAttachment( const RenderTarget::Attachment& atta
|
||||
deviceMemory = attachment.mem;
|
||||
|
||||
// Create sampler
|
||||
if ( createSampler ) sampler.initialize( ext::vulkan::device );
|
||||
if ( createSampler ) {
|
||||
// sampler.initialize( ext::vulkan::device );
|
||||
sampler = ext::vulkan::Sampler::retrieve( sampler.descriptor );
|
||||
}
|
||||
|
||||
this->updateDescriptors();
|
||||
}
|
||||
|
||||
@ -378,23 +378,14 @@ void ext::vulkan::destroy() {
|
||||
Texture2D::empty.destroy();
|
||||
Texture3D::empty.destroy();
|
||||
TextureCube::empty.destroy();
|
||||
if ( uf::scene::useGraph ) {
|
||||
|
||||
auto graph = uf::scene::generateGraph();
|
||||
for ( auto entity : graph ) {
|
||||
if ( !entity->hasComponent<uf::Graphic>() ) continue;
|
||||
uf::Graphic& graphic = entity->getComponent<uf::Graphic>();
|
||||
graphic.destroy();
|
||||
}
|
||||
} else {
|
||||
for ( uf::Scene* scene : uf::scene::scenes ) {
|
||||
if ( !scene ) continue;
|
||||
scene->process([&]( uf::Entity* entity ) {
|
||||
if ( !entity->hasComponent<uf::Graphic>() ) return;
|
||||
uf::Graphic& graphic = entity->getComponent<uf::Graphic>();
|
||||
graphic.destroy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto& renderMode : renderModes ) {
|
||||
if ( !renderMode ) continue;
|
||||
renderMode->destroy();
|
||||
@ -402,6 +393,10 @@ if ( uf::scene::useGraph ) {
|
||||
renderMode = NULL;
|
||||
}
|
||||
|
||||
for ( auto& s : ext::vulkan::Sampler::samplers ) {
|
||||
s.destroy();
|
||||
}
|
||||
|
||||
swapchain.destroy();
|
||||
device.destroy();
|
||||
ext::vulkan::mutex.unlock();
|
||||
|
||||
@ -11,7 +11,7 @@ void spec::win32::controller::tick() {}
|
||||
void spec::win32::controller::terminate() {}
|
||||
bool spec::win32::controller::connected( size_t i ) {
|
||||
#if UF_USE_OPENVR
|
||||
if ( ext::openvr::controllerActive( i ) ) return true;
|
||||
if ( ext::openvr::controllerActive( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right ) ) return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@ -50,12 +50,13 @@ bool spec::win32::controller::pressed( const std::string& _name, size_t i ) {
|
||||
else if ( name == "A" && i == vr::Controller_Hand::Hand_Left ) key = "a";
|
||||
else if ( name == "B" && i == vr::Controller_Hand::Hand_Left ) key = "b";
|
||||
|
||||
if ( name != "" ) return ext::openvr::controllerState( i, key )["state"].as<bool>();
|
||||
if ( name != "" ) return ext::openvr::controllerState( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right, key )["state"].as<bool>();
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
float spec::win32::controller::analog( const std::string&, size_t i ) {
|
||||
float spec::win32::controller::analog( const std::string& _name, size_t i ) {
|
||||
std::string name = uf::string::uppercase(_name);
|
||||
#if UF_USE_OPENVR
|
||||
if ( ext::openvr::context ) {
|
||||
size_t offset = 0;
|
||||
@ -70,7 +71,7 @@ float spec::win32::controller::analog( const std::string&, size_t i ) {
|
||||
else if ( name == "L_JOYSTICK_Y" ) { i = vr::Controller_Hand::Hand_Left; key = "thumbstick"; offset = 1; }
|
||||
else if ( name == "JOYSTICK_Y" && i == vr::Controller_Hand::Hand_Left ) { key = "thumbstick"; offset = 1; }
|
||||
|
||||
if ( name != "" ) return ext::openvr::controllerState( i, key )["analog"]["position"][offset].as<float>();
|
||||
if ( name != "" ) return ext::openvr::controllerState( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right, key )["analog"]["position"][offset].as<float>();
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@ -992,8 +992,9 @@ void UF_API_CALL spec::win32::Window::processEvent(UINT message, WPARAM wParam,
|
||||
{
|
||||
json["type"] = event.type;
|
||||
json["invoker"] = event.invoker;
|
||||
json["window"]["size"]["x"] = event.window.size.x;
|
||||
json["window"]["size"]["y"] = event.window.size.y;
|
||||
json["window"]["size"] = uf::vector::encode(event.window.size);
|
||||
// json["window"]["size"]["x"] = event.window.size.x;
|
||||
// json["window"]["size"]["y"] = event.window.size.y;
|
||||
this->pushEvent(event.type, json);
|
||||
}
|
||||
|
||||
@ -1028,8 +1029,9 @@ void UF_API_CALL spec::win32::Window::processEvent(UINT message, WPARAM wParam,
|
||||
{
|
||||
json["type"] = event.type;
|
||||
json["invoker"] = event.invoker;
|
||||
json["window"]["size"]["x"] = event.window.size.x;
|
||||
json["window"]["size"]["y"] = event.window.size.y;
|
||||
json["window"]["size"] = uf::vector::encode(event.window.size);
|
||||
// json["window"]["size"]["x"] = event.window.size.x;
|
||||
// json["window"]["size"]["y"] = event.window.size.y;
|
||||
this->pushEvent(event.type, json);
|
||||
}
|
||||
} else {
|
||||
@ -1255,8 +1257,9 @@ void UF_API_CALL spec::win32::Window::processEvent(UINT message, WPARAM wParam,
|
||||
json["type"] = event.type;
|
||||
json["invoker"] = event.invoker;
|
||||
|
||||
json["mouse"]["position"]["x"] = event.mouse.position.x;
|
||||
json["mouse"]["position"]["y"] = event.mouse.position.y;
|
||||
json["mouse"]["position"] = uf::vector::encode(event.mouse.position);
|
||||
// json["mouse"]["position"]["x"] = event.mouse.position.x;
|
||||
// json["mouse"]["position"]["y"] = event.mouse.position.y;
|
||||
json["mouse"]["delta"] = event.mouse.delta;
|
||||
|
||||
this->pushEvent(event.type, json);
|
||||
@ -1322,13 +1325,15 @@ void UF_API_CALL spec::win32::Window::processEvent(UINT message, WPARAM wParam,
|
||||
#if USE_OPTIMAL
|
||||
this->pushEvent(event.type, uf::Userdata(uf::userdata::create(event)));
|
||||
#endif
|
||||
json["type"] = event.type;
|
||||
json["type"] = event.type;
|
||||
json["invoker"] = event.invoker;
|
||||
|
||||
json["mouse"]["position"]["x"] = event.mouse.position.x;
|
||||
json["mouse"]["position"]["y"] = event.mouse.position.y;
|
||||
json["mouse"]["delta"]["x"] = event.mouse.delta.x;
|
||||
json["mouse"]["delta"]["y"] = event.mouse.delta.y;
|
||||
json["mouse"]["position"] = uf::vector::encode(event.mouse.position);
|
||||
json["mouse"]["delta"] = uf::vector::encode(event.mouse.delta);
|
||||
// json["mouse"]["position"]["x"] = event.mouse.position.x;
|
||||
// json["mouse"]["position"]["y"] = event.mouse.position.y;
|
||||
// json["mouse"]["delta"]["x"] = event.mouse.delta.x;
|
||||
// json["mouse"]["delta"]["y"] = event.mouse.delta.y;
|
||||
json["mouse"]["button"] = event.mouse.button;
|
||||
switch (event.mouse.state) {
|
||||
case 1:
|
||||
@ -1441,12 +1446,15 @@ void UF_API_CALL spec::win32::Window::processEvent(UINT message, WPARAM wParam,
|
||||
json["type"] = event.type;
|
||||
json["invoker"] = event.invoker;
|
||||
|
||||
json["mouse"]["position"]["x"] = event.mouse.position.x;
|
||||
json["mouse"]["position"]["y"] = event.mouse.position.y;
|
||||
json["mouse"]["delta"]["x"] = event.mouse.delta.x;
|
||||
json["mouse"]["delta"]["y"] = event.mouse.delta.y;
|
||||
json["mouse"]["size"]["x"] = event.mouse.size.x;
|
||||
json["mouse"]["size"]["y"] = event.mouse.size.y;
|
||||
// json["mouse"]["position"]["x"] = event.mouse.position.x;
|
||||
// json["mouse"]["position"]["y"] = event.mouse.position.y;
|
||||
// json["mouse"]["delta"]["x"] = event.mouse.delta.x;
|
||||
// json["mouse"]["delta"]["y"] = event.mouse.delta.y;
|
||||
// json["mouse"]["size"]["x"] = event.mouse.size.x;
|
||||
// json["mouse"]["size"]["y"] = event.mouse.size.y;
|
||||
json["mouse"]["position"] = uf::vector::encode(event.mouse.position);
|
||||
json["mouse"]["delta"] = uf::vector::encode(event.mouse.delta);
|
||||
json["mouse"]["size"] = uf::vector::encode(event.mouse.size);
|
||||
switch (event.mouse.state) {
|
||||
case 1:
|
||||
json["mouse"]["state"] = "Entered";
|
||||
|
||||
@ -116,6 +116,16 @@ std::vector<uint8_t> UF_API uf::io::readAsBuffer( const std::string& filename, c
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
size_t UF_API uf::io::write( const std::string& filename, const void* buffer, size_t size ) {
|
||||
std::ofstream output;
|
||||
output.open( uf::io::sanitize( filename ), std::ios::binary);
|
||||
output.write( (const char*) buffer, size );
|
||||
output.close();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
std::string UF_API uf::io::hash( const std::string& filename ) {
|
||||
return uf::string::sha256( uf::io::readAsBuffer( filename ) );
|
||||
}
|
||||
|
||||
@ -55,42 +55,59 @@ size_t UF_API_CALL uf::Window::getRefreshRate() const {
|
||||
}
|
||||
// Attribute modifiers
|
||||
void UF_API_CALL uf::Window::setPosition( const spec::uni::Window::vector_t& position ) {
|
||||
if ( this->m_window ) this->m_window->setPosition(position);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setPosition(position);
|
||||
}
|
||||
void UF_API_CALL uf::Window::centerWindow() {
|
||||
if ( this->m_window ) this->m_window->centerWindow();
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->centerWindow();
|
||||
}
|
||||
void UF_API_CALL uf::Window::setMousePosition( const spec::uni::Window::vector_t& position ) {
|
||||
if ( this->m_window ) this->m_window->setMousePosition(position);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setMousePosition(position);
|
||||
}
|
||||
spec::uni::Window::vector_t UF_API_CALL uf::Window::getMousePosition() {
|
||||
if ( this->m_window ) return this->m_window->getMousePosition();
|
||||
return { 0, 0 };
|
||||
return this->m_window ? this->m_window->getMousePosition() : spec::uni::Window::vector_t{ 0, 0 };
|
||||
}
|
||||
void UF_API_CALL uf::Window::setSize( const spec::uni::Window::vector_t& size ) {
|
||||
if ( this->m_window ) this->m_window->setSize(size);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setSize(size);
|
||||
}
|
||||
void UF_API_CALL uf::Window::setTitle( const spec::uni::Window::title_t& title ) {
|
||||
if ( this->m_window ) this->m_window->setTitle(title);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setTitle(title);
|
||||
|
||||
uf::Serializer json;
|
||||
std::string hook = "window:Title.Changed";
|
||||
json["type"] = hook;
|
||||
json["invoker"] = "os";
|
||||
json["window"]["title"] = std::string(title);
|
||||
uf::hooks.call( hook, json );
|
||||
}
|
||||
void UF_API_CALL uf::Window::setIcon( const spec::uni::Window::vector_t& size, uint8_t* pixels ) {
|
||||
if ( this->m_window ) this->m_window->setIcon(size, pixels);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setIcon(size, pixels);
|
||||
}
|
||||
void UF_API_CALL uf::Window::setVisible( bool visibility ) {
|
||||
if ( this->m_window ) this->m_window->setVisible(visibility);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setVisible(visibility);
|
||||
}
|
||||
void UF_API_CALL uf::Window::setCursorVisible( bool visibility ) {
|
||||
if ( this->m_window ) this->m_window->setCursorVisible(visibility);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setCursorVisible(visibility);
|
||||
}
|
||||
void UF_API_CALL uf::Window::setKeyRepeatEnabled( bool state ) {
|
||||
if ( this->m_window ) this->m_window->setKeyRepeatEnabled(state);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setKeyRepeatEnabled(state);
|
||||
}
|
||||
void UF_API_CALL uf::Window::setMouseGrabbed( bool state ) {
|
||||
if ( this->m_window ) this->m_window->setMouseGrabbed(state);
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->setMouseGrabbed(state);
|
||||
}
|
||||
|
||||
void UF_API_CALL uf::Window::requestFocus() {
|
||||
if ( this->m_window ) this->m_window->requestFocus();
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->requestFocus();
|
||||
}
|
||||
bool UF_API_CALL uf::Window::hasFocus() const {
|
||||
return uf::Window::focused = (this->m_window ? this->m_window->hasFocus() : false);
|
||||
@ -99,11 +116,13 @@ pod::Vector2ui UF_API_CALL uf::Window::getResolution() {
|
||||
return uf::Window::window_t::getResolution();
|
||||
}
|
||||
void UF_API_CALL uf::Window::switchToFullscreen( bool borderless ) {
|
||||
if ( this->m_window ) this->m_window->switchToFullscreen( borderless );
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->switchToFullscreen( borderless );
|
||||
}
|
||||
// Update
|
||||
void UF_API_CALL uf::Window::processEvents() {
|
||||
if ( this->m_window ) this->m_window->processEvents();
|
||||
if ( !this->m_window ) return;
|
||||
this->m_window->processEvents();
|
||||
}
|
||||
bool UF_API_CALL uf::Window::pollEvents( bool block ) {
|
||||
return this->m_window ? this->m_window->pollEvents(block) : false;
|
||||
@ -113,8 +132,8 @@ bool UF_API_CALL uf::Window::isKeyPressed( const std::string& key ) {
|
||||
}
|
||||
bool UF_API_CALL uf::Window::setActive(bool active) {
|
||||
#if UF_USE_OPENGL && UF_OPENGL_CONTEXT_IN_WINDOW
|
||||
if (this->m_context) {
|
||||
if (this->m_context->setActive(active)) return true;
|
||||
if ( this->m_context ) {
|
||||
if ( this->m_context->setActive(active) ) return true;
|
||||
uf::iostream << "[" << uf::IoStream::Color()("Red") << "ERROR" << "]" << "Failed to activate the window's context" << "\n";
|
||||
return false;
|
||||
}
|
||||
@ -144,21 +163,8 @@ void UF_API_CALL uf::Window::createSurface( VkInstance instance, VkSurfaceKHR& s
|
||||
#include <chrono>
|
||||
#include <uf/utils/time/time.h>
|
||||
void UF_API_CALL uf::Window::display() {
|
||||
// Display the backbuffer on screen
|
||||
#if UF_USE_OPENGL && UF_OPENGL_CONTEXT_IN_WINDOW
|
||||
if (this->m_context && this->setActive()) this->m_context->display();
|
||||
#endif
|
||||
#if 0
|
||||
/* FPS */ if ( false ) {
|
||||
static double limit = 1.0 / 60;
|
||||
static uf::Timer<long long> timer(false);
|
||||
if ( !timer.running() ) timer.start();
|
||||
double delta = limit - timer.elapsed().asDouble();
|
||||
if ( delta > 0 ) {
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( (int) delta * 1000 ) );
|
||||
timer.reset();
|
||||
}
|
||||
}
|
||||
if ( this->m_context && this->setActive() ) this->m_context->display();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -69,8 +69,8 @@ void ext::PlayerHandBehavior::initialize( uf::Object& self ) {
|
||||
graphic.process = true;
|
||||
|
||||
graphic.descriptor.frontFace = uf::renderer::enums::Face::CCW;
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/base.vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
|
||||
uf::instantiator::bind( "EntityBehavior", hand );
|
||||
uf::instantiator::bind( "ObjectBehavior", hand );
|
||||
@ -104,8 +104,8 @@ void ext::PlayerHandBehavior::initialize( uf::Object& self ) {
|
||||
graphic.initialize();
|
||||
graphic.initializeMesh(mesh);
|
||||
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/line/vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/line/frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/line/base.vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/line/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
graphic.descriptor.topology = uf::renderer::enums::PrimitiveTopology::LINE_STRIP;
|
||||
graphic.descriptor.fill = uf::renderer::enums::PolygonMode::LINE;
|
||||
graphic.descriptor.lineWidth = metadata["hands"][side]["pointer"]["width"].as<float>();
|
||||
@ -166,10 +166,12 @@ void ext::PlayerHandBehavior::initialize( uf::Object& self ) {
|
||||
uf::Serializer payload;
|
||||
payload["type"] = "window:Mouse.Click";
|
||||
payload["invoker"] = "vr";
|
||||
payload["mouse"]["position"]["x"] = metadata["hands"][side]["cursor"]["position"][0].as<float>();
|
||||
payload["mouse"]["position"]["y"] = metadata["hands"][side]["cursor"]["position"][1].as<float>();
|
||||
payload["mouse"]["delta"]["x"] = 0;
|
||||
payload["mouse"]["delta"]["y"] = 0;
|
||||
// payload["mouse"]["position"]["x"] = metadata["hands"][side]["cursor"]["position"][0].as<float>();
|
||||
// payload["mouse"]["position"]["y"] = metadata["hands"][side]["cursor"]["position"][1].as<float>();
|
||||
// payload["mouse"]["delta"]["x"] = 0;
|
||||
// payload["mouse"]["delta"]["y"] = 0;
|
||||
payload["mouse"]["position"] = metadata["hands"][side]["cursor"]["position"];
|
||||
payload["mouse"]["delta"] = uf::vector::encode( pod::Vector2i{0,0} );
|
||||
payload["mouse"]["button"] = side == "left" ? "Right" : "Left";
|
||||
payload["mouse"]["state"] = json["state"].as<bool>() ? "Down": "Up";
|
||||
|
||||
|
||||
@ -244,31 +244,32 @@ void ext::LightBehavior::tick( uf::Object& self ) {
|
||||
camera.updateView();
|
||||
}
|
||||
}
|
||||
bool execute = true;
|
||||
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
|
||||
// enable renderer every X seconds
|
||||
if ( metadata.renderer.limiter > 0 ) {
|
||||
if ( metadata.renderer.timer > metadata.renderer.limiter ) {
|
||||
metadata.renderer.timer = 0;
|
||||
execute = true;
|
||||
renderMode.execute = true;
|
||||
} else {
|
||||
metadata.renderer.timer = metadata.renderer.timer + uf::physics::time::delta;
|
||||
execute = false;
|
||||
renderMode.execute = false;
|
||||
}
|
||||
} else {
|
||||
// round robin, enable if it's the light's current turn
|
||||
if ( metadata.renderer.mode == "round robin" ) {
|
||||
if ( ::roundRobin.current < ::roundRobin.lights.size() ) execute = ::roundRobin.lights[::roundRobin.current] == this;
|
||||
if ( ::roundRobin.current < ::roundRobin.lights.size() )
|
||||
renderMode.execute = ::roundRobin.lights[::roundRobin.current] == this;
|
||||
// render only if the light is used
|
||||
} else if ( metadata.renderer.mode == "occlusion" ) {
|
||||
execute = metadata.renderer.rendered;
|
||||
renderMode.execute = metadata.renderer.rendered;
|
||||
// light baking, but sadly re-bakes every time the command buffer is recorded
|
||||
} else if ( metadata.renderer.mode == "once" ) {
|
||||
execute = !metadata.renderer.rendered;
|
||||
renderMode.execute = !metadata.renderer.rendered;
|
||||
metadata.renderer.rendered = true;
|
||||
} else if ( metadata.renderer.mode == "in-range" ) {
|
||||
metadata.renderer.rendered = false;
|
||||
}
|
||||
}
|
||||
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
|
||||
renderMode.execute = execute;
|
||||
}
|
||||
#if UF_ENTITY_METADATA_USE_JSON
|
||||
metadata.serialize();
|
||||
|
||||
@ -80,8 +80,8 @@ void ext::NoiseBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
this->callHook("entity:TextureUpdate.%UID%");
|
||||
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/noise/frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/base.vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/noise/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
}
|
||||
void ext::NoiseBehavior::tick( uf::Object& self ) {
|
||||
auto& metadata = this->getComponent<uf::Serializer>();
|
||||
|
||||
@ -40,8 +40,23 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
uf::Camera& camera = this->getComponent<uf::Camera>();
|
||||
settings.mode = metadataJson["camera"]["ortho"].as<bool>() ? -1 : 1;
|
||||
settings.perspective.size.x = metadataJson["camera"]["settings"]["size"]["x"].as<double>();
|
||||
settings.perspective.size.y = metadataJson["camera"]["settings"]["size"]["y"].as<double>();
|
||||
|
||||
settings.perspective.size = uf::vector::decode( metadataJson["camera"]["settings"]["size"], pod::Vector3f{} );
|
||||
// Update viewport
|
||||
if ( settings.perspective.size.x <= 0 || settings.perspective.size.y <= 0 ) {
|
||||
settings.perspective.size = uf::vector::decode( ext::config["window"]["size"], pod::Vector2ui{} );
|
||||
this->addHook( "window:Resized", [&](ext::json::Value& json){
|
||||
// Update persistent window sized (size stored to JSON file)
|
||||
pod::Vector2ui size = uf::vector::decode( json["window"]["size"], pod::Vector2ui{} ); {
|
||||
// size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
// size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
}
|
||||
/* Update camera's viewport */ {
|
||||
uf::Camera& camera = this->getComponent<uf::Camera>();
|
||||
camera.setSize({(pod::Math::num_t)size.x, (pod::Math::num_t)size.y});
|
||||
}
|
||||
} );
|
||||
}
|
||||
camera.setSize(settings.perspective.size);
|
||||
if ( settings.mode < 0 ) {
|
||||
settings.ortho.lr.x = metadataJson["camera"]["settings"]["left"].as<double>();
|
||||
@ -76,24 +91,8 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
|
||||
transform.scale.y = metadataJson["camera"]["scale"][1].as<double>();
|
||||
transform.scale.z = metadataJson["camera"]["scale"][2].as<double>();
|
||||
}
|
||||
|
||||
camera.setOffset(settings.offset);
|
||||
camera.update(true);
|
||||
|
||||
// Update viewport
|
||||
if ( metadataJson["camera"]["settings"]["size"]["auto"].as<bool>() ) {
|
||||
this->addHook( "window:Resized", [&](ext::json::Value& json){
|
||||
// Update persistent window sized (size stored to JSON file)
|
||||
pod::Vector2ui size; {
|
||||
size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
}
|
||||
/* Update camera's viewport */ {
|
||||
uf::Camera& camera = this->getComponent<uf::Camera>();
|
||||
camera.setSize({(pod::Math::num_t)size.x, (pod::Math::num_t)size.y});
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
metadataJson["system"]["control"] = true;
|
||||
this->addHook( "window:Mouse.CursorVisibility", [&](ext::json::Value& json){
|
||||
@ -106,8 +105,10 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
|
||||
if ( !ext::json::isObject(json) ) return;
|
||||
if ( json["invoker"] != "client" ) return;
|
||||
|
||||
pod::Vector2i delta = { json["mouse"]["delta"]["x"].as<int>(), json["mouse"]["delta"]["y"].as<int>() };
|
||||
pod::Vector2i size = { json["mouse"]["size"]["x"].as<int>(), json["mouse"]["size"]["y"].as<int>() };
|
||||
// pod::Vector2i delta = { json["mouse"]["delta"]["x"].as<int>(), json["mouse"]["delta"]["y"].as<int>() };
|
||||
// pod::Vector2i size = { json["mouse"]["size"]["x"].as<int>(), json["mouse"]["size"]["y"].as<int>() };
|
||||
pod::Vector2i delta = uf::vector::decode( json["mouse"]["delta"], pod::Vector2i{} );
|
||||
pod::Vector2i size = uf::vector::decode( json["mouse"]["size"], pod::Vector2i{} );
|
||||
pod::Vector2 relta = { (float) delta.x / size.x, (float) delta.y / size.y };
|
||||
relta *= 2;
|
||||
if ( delta.x == 0 && delta.y == 0 ) return;
|
||||
@ -379,10 +380,10 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
#if UF_ENTITY_METADATA_USE_JSON
|
||||
metadata.deserialize();
|
||||
#endif
|
||||
stats.floored = fabs(physics.linear.velocity.y) < 0.01f;
|
||||
stats.menu = metadata.system.menu;
|
||||
stats.impulse = metadata.system.physics.impulse;
|
||||
stats.noclipped = metadata.system.noclipped;
|
||||
stats.floored = fabs(physics.linear.velocity.y) < 0.01f || stats.noclipped;
|
||||
struct {
|
||||
float move = 4;
|
||||
float walk = 1;
|
||||
@ -394,6 +395,11 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
speed.move = metadata.system.physics.move;
|
||||
speed.run = metadata.system.physics.run / speed.move;
|
||||
speed.walk = metadata.system.physics.walk / speed.move;
|
||||
|
||||
if ( stats.noclipped ) {
|
||||
speed.move *= 4.0;
|
||||
speed.run *= 2.0;
|
||||
}
|
||||
}
|
||||
if ( !metadata.system.physics.collision ) {
|
||||
stats.impulse = true;
|
||||
@ -449,23 +455,31 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
|
||||
if ( stats.floored ) {
|
||||
pod::Transform<> translator = transform;
|
||||
|
||||
#if UF_USE_OPENVR
|
||||
if ( ext::openvr::context ) {
|
||||
bool useController = true;
|
||||
translator.orientation = uf::quaternion::multiply( transform.orientation * pod::Vector4f{1,1,1,1}, useController ? (ext::openvr::controllerQuaternion( vr::Controller_Hand::Hand_Right ) * pod::Vector4f{1,1,1,1}) : ext::openvr::hmdQuaternion() );
|
||||
translator = uf::transform::reorient( translator );
|
||||
|
||||
translator.forward *= { 1, 0, 1 };
|
||||
translator.right *= { 1, 0, 1 };
|
||||
|
||||
if ( !stats.noclipped ) {
|
||||
translator.forward *= { 1, 0, 1 };
|
||||
translator.right *= { 1, 0, 1 };
|
||||
}
|
||||
|
||||
translator.forward = uf::vector::normalize( translator.forward );
|
||||
translator.right = uf::vector::normalize( translator.right );
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if ( stats.noclipped ){
|
||||
auto& cameraTransform = camera.getTransform();
|
||||
// translator = uf::transform::flatten( cameraTransform );
|
||||
translator.forward.y += cameraTransform.forward.y;
|
||||
}
|
||||
pod::Vector3f queued = {};
|
||||
if ( keys.forward || keys.backwards ) {
|
||||
int polarity = keys.forward ? 1 : -1;
|
||||
float mag = uf::vector::magnitude(physics.linear.velocity * pod::Vector3{1, 0, 1});
|
||||
float mag = uf::vector::magnitude(physics.linear.velocity); // * pod::Vector3{1, 0, 1});
|
||||
if ( mag < speed.limitSquared ) {
|
||||
mag = uf::vector::magnitude(physics.linear.velocity + translator.forward * speed.move * polarity);
|
||||
} else mag = speed.limitSquared;
|
||||
@ -477,17 +491,21 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
if ( stats.impulse && stats.noclipped ) {
|
||||
physics.linear.velocity.x = correction.x;
|
||||
physics.linear.velocity.z = correction.z;
|
||||
|
||||
if ( stats.noclipped ) physics.linear.velocity.y = correction.y;
|
||||
} else {
|
||||
correction *= uf::physics::time::delta;
|
||||
transform.position.x += correction.x;
|
||||
transform.position.z += correction.z;
|
||||
|
||||
if ( stats.noclipped ) transform.position.y += correction.y;
|
||||
}
|
||||
}
|
||||
stats.updateCamera = (stats.walking = true);
|
||||
}
|
||||
if ( keys.left || keys.right ) {
|
||||
int polarity = keys.right ? 1 : -1;
|
||||
float mag = uf::vector::magnitude(physics.linear.velocity * pod::Vector3{1, 0, 1});
|
||||
float mag = uf::vector::magnitude(physics.linear.velocity); // * pod::Vector3{1, 0, 1});
|
||||
if ( mag < speed.limitSquared ) {
|
||||
mag = uf::vector::magnitude(physics.linear.velocity + translator.right * speed.move * polarity);
|
||||
} else mag = speed.limitSquared;
|
||||
@ -511,6 +529,8 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
if ( collider.body && !collider.shared ) {
|
||||
physics.linear.velocity.x = queued.x;
|
||||
physics.linear.velocity.z = queued.z;
|
||||
|
||||
if ( stats.noclipped ) physics.linear.velocity.y = queued.y;
|
||||
#if UF_USE_BULLET
|
||||
ext::bullet::move( collider, physics.linear.velocity );
|
||||
#endif
|
||||
@ -520,6 +540,8 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
if ( collider.body && !collider.shared ) {
|
||||
physics.linear.velocity.x = 0;
|
||||
physics.linear.velocity.z = 0;
|
||||
|
||||
if ( stats.noclipped ) physics.linear.velocity.y = 0;
|
||||
#if UF_USE_BULLET
|
||||
ext::bullet::move( collider, physics.linear.velocity );
|
||||
#endif
|
||||
|
||||
@ -108,9 +108,9 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
|
||||
});
|
||||
/* store viewport size */
|
||||
this->addHook( "window:Resized", [&](ext::json::Value& json){
|
||||
pod::Vector2ui size; {
|
||||
size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
pod::Vector2ui size = uf::vector::decode( json["window"]["size"], pod::Vector2i{} ); {
|
||||
// size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
// size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
}
|
||||
|
||||
metadataJson["system"]["window"] = json["system"]["window"];
|
||||
@ -140,7 +140,7 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
|
||||
float high = std::numeric_limits<float>::min();
|
||||
float low = std::numeric_limits<float>::max();
|
||||
float amplitude = metadataJson["noise"]["amplitude"].is<float>() ? metadataJson["noise"]["amplitude"].as<float>() : 1.5;
|
||||
pod::Vector3ui size = uf::vector::decode(metadataJson["noise"]["size"], pod::Vector3ui{256, 256, 256});
|
||||
pod::Vector3ui size = uf::vector::decode(metadataJson["noise"]["size"], pod::Vector3ui{64, 64, 64});
|
||||
pod::Vector3d coefficients = uf::vector::decode(metadataJson["noise"]["coefficients"], pod::Vector3d{3.0, 3.0, 3.0});
|
||||
|
||||
std::vector<uint8_t> pixels(size.x * size.y * size.z);
|
||||
@ -169,8 +169,6 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
|
||||
}
|
||||
texture.fromBuffers( (void*) pixels.data(), pixels.size(), uf::renderer::enums::Format::R8_UNORM, size.x, size.y, size.z, 1 );
|
||||
}
|
||||
// initialize voxel map
|
||||
|
||||
// initialize cubemap
|
||||
{
|
||||
std::vector<std::string> filenames = {
|
||||
@ -525,7 +523,10 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
|
||||
auto& metadata = entity->getComponent<ext::LightBehavior::Metadata>();
|
||||
if ( entity->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
|
||||
auto& renderMode = entity->getComponent<uf::renderer::RenderTargetRenderMode>();
|
||||
if ( metadata.renderer.mode == "in-range" ) renderMode.execute = false;
|
||||
if ( metadata.renderer.mode == "in-range" ) {
|
||||
// UF_DEBUG_MSG( renderModeName << "\t" << entity->getName() << "\t" << renderMode.execute );
|
||||
renderMode.execute = false;
|
||||
}
|
||||
}
|
||||
if ( metadata.power <= 0 ) continue;
|
||||
LightInfo& info = entities.emplace_back();
|
||||
@ -548,12 +549,14 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
|
||||
int shadowThreshold = metadata.light.shadowThreshold;
|
||||
if ( shadowSamples < 0 ) shadowSamples = 0;
|
||||
if ( shadowThreshold <= 0 ) shadowThreshold = std::numeric_limits<int>::max();
|
||||
|
||||
{
|
||||
std::vector<LightInfo> scratch;
|
||||
scratch.reserve(entities.size());
|
||||
for ( size_t i = 0; i < entities.size(); ++i ) {
|
||||
auto& info = entities[i];
|
||||
if ( info.shadows && --shadowThreshold <= 0 ) info.shadows = false;
|
||||
if ( renderModeName != "" ) info.shadows = false;
|
||||
scratch.emplace_back(info);
|
||||
}
|
||||
entities = scratch;
|
||||
@ -610,16 +613,24 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
|
||||
|
||||
graphic.material.textures.clear();
|
||||
if ( uf::renderer::settings::experimental::deferredMode == "vxgi" ) {
|
||||
for ( auto& t : sceneTextures.voxels.id ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.uv ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.normal ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radiance ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.id ) {
|
||||
graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
}
|
||||
for ( auto& t : sceneTextures.voxels.uv ) {
|
||||
graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
}
|
||||
for ( auto& t : sceneTextures.voxels.normal ) {
|
||||
graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
}
|
||||
for ( auto& t : sceneTextures.voxels.radiance ) {
|
||||
graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
}
|
||||
}
|
||||
|
||||
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.noise); //this->getComponent<uf::renderer::Texture3D>());
|
||||
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.skybox); //this->getComponent<uf::renderer::TextureCube>());
|
||||
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.noise);
|
||||
graphic.material.textures.emplace_back().aliasTexture(sceneTextures.skybox);
|
||||
|
||||
size_t updateThreshold = metadata.light.updateThreshold;
|
||||
int32_t updateThreshold = metadata.light.updateThreshold;
|
||||
size_t textureSlot = 0;
|
||||
|
||||
std::vector<pod::Light::Storage> lights;
|
||||
@ -686,6 +697,7 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
|
||||
auto& renderMode = entity->getComponent<uf::renderer::RenderTargetRenderMode>();
|
||||
if ( metadata.renderer.mode == "in-range" && --updateThreshold > 0 ) {
|
||||
renderMode.execute = true;
|
||||
// UF_DEBUG_MSG( renderModeName << "\t" << entity->getName() << "\t" << renderMode.execute );
|
||||
}
|
||||
size_t view = 0;
|
||||
for ( auto& attachment : renderMode.renderTarget.attachments ) {
|
||||
@ -706,6 +718,7 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& re
|
||||
lights.emplace_back(light);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uniforms->lengths.lights = std::min( lights.size(), metadata.max.lights );
|
||||
bool shouldUpdate = graphic.material.textures.size() != previousTextures.size();
|
||||
|
||||
@ -82,8 +82,8 @@ void ext::HousamoSpriteBehavior::initialize( uf::Object& self ) {
|
||||
auto& texture = graphic.material.textures.emplace_back();
|
||||
texture.loadFromImage( image );
|
||||
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/base.vert.spv", uf::renderer::enums::Shader::VERTEX);
|
||||
graphic.material.attachShader(uf::io::root+"/shaders/base/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT);
|
||||
|
||||
metadata["system"]["control"] = true;
|
||||
metadata["system"]["loaded"] = true;
|
||||
|
||||
@ -50,11 +50,13 @@ void ext::VoxelizerBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
std::vector<uint8_t> empty(metadata.voxelSize.x * metadata.voxelSize.y * metadata.voxelSize.z * sizeof(uint8_t) * 4);
|
||||
|
||||
const bool HDR = false;
|
||||
for ( size_t i = 0; i < metadata.cascades; ++i ) {
|
||||
auto& id = sceneTextures.voxels.id.emplace_back();
|
||||
auto& uv = sceneTextures.voxels.uv.emplace_back();
|
||||
auto& normal = sceneTextures.voxels.normal.emplace_back();
|
||||
auto& radiance = sceneTextures.voxels.radiance.emplace_back();
|
||||
// auto& depth = sceneTextures.voxels.depth.emplace_back();
|
||||
|
||||
id.sampler.descriptor.filter.min = VK_FILTER_NEAREST;
|
||||
id.sampler.descriptor.filter.mag = VK_FILTER_NEAREST;
|
||||
@ -62,7 +64,8 @@ void ext::VoxelizerBehavior::initialize( uf::Object& self ) {
|
||||
id.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R16G16_UINT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
|
||||
uv.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R16G16_SFLOAT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
|
||||
normal.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R16G16_SFLOAT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
|
||||
radiance.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R8G8B8A8_UNORM, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
|
||||
radiance.fromBuffers( (void*) empty.data(), empty.size(), HDR ? uf::renderer::enums::Format::R16G16B16A16_SFLOAT : uf::renderer::enums::Format::R8G8B8A8_UNORM, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
|
||||
// depth.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R16_SFLOAT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
|
||||
}
|
||||
}
|
||||
// initialize render mode
|
||||
@ -74,7 +77,7 @@ void ext::VoxelizerBehavior::initialize( uf::Object& self ) {
|
||||
metadata.renderModeName = "VXGI:" + std::to_string((int) this->getUid());
|
||||
uf::renderer::addRenderMode( &renderMode, metadata.renderModeName );
|
||||
renderMode.metadata["type"] = "vxgi";
|
||||
renderMode.metadata["samples"] = 2;
|
||||
renderMode.metadata["samples"] = 1;
|
||||
renderMode.metadata["subpasses"] = metadata.cascades;
|
||||
|
||||
renderMode.blitter.device = &ext::vulkan::device;
|
||||
@ -108,41 +111,7 @@ void ext::VoxelizerBehavior::initialize( uf::Object& self ) {
|
||||
for ( auto& t : sceneTextures.voxels.normal ) vkCmdClearColorImage( commandBuffer, t.image, t.imageLayout, &clearColor, 1, &subresourceRange );
|
||||
for ( auto& t : sceneTextures.voxels.uv ) vkCmdClearColorImage( commandBuffer, t.image, t.imageLayout, &clearColor, 1, &subresourceRange );
|
||||
for ( auto& t : sceneTextures.voxels.radiance ) vkCmdClearColorImage( commandBuffer, t.image, t.imageLayout, &clearColor, 1, &subresourceRange );
|
||||
|
||||
#if 0
|
||||
for ( auto& t : sceneTextures.voxels.radiance ) {
|
||||
VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
|
||||
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED
|
||||
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED
|
||||
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
|
||||
imageMemoryBarrier.subresourceRange.levelCount = t.mips;
|
||||
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
|
||||
imageMemoryBarrier.subresourceRange.layerCount = 1;
|
||||
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
{
|
||||
VkPipelineStageFlags srcStageMask, dstStageMask;
|
||||
imageMemoryBarrier.image = t.image;
|
||||
imageMemoryBarrier.oldLayout = t.imageLayout;
|
||||
imageMemoryBarrier.newLayout = t.imageLayout;
|
||||
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
|
||||
vkCmdPipelineBarrier( commandBuffer,
|
||||
srcStageMask, dstStageMask,
|
||||
VK_FLAGS_NONE,
|
||||
0, NULL,
|
||||
0, NULL,
|
||||
1, &imageMemoryBarrier
|
||||
);
|
||||
|
||||
t.imageLayout = imageMemoryBarrier.newLayout;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// for ( auto& t : sceneTextures.voxels.depth ) vkCmdClearColorImage( commandBuffer, t.image, t.imageLayout, &clearColor, 1, &subresourceRange );
|
||||
});
|
||||
renderMode.bindCallback( renderMode.CALLBACK_END, [&]( VkCommandBuffer commandBuffer ){
|
||||
// parse voxel lighting
|
||||
@ -176,42 +145,6 @@ void ext::VoxelizerBehavior::initialize( uf::Object& self ) {
|
||||
subresourceRange
|
||||
);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// sync
|
||||
for ( auto& t : sceneTextures.voxels.radiance ) {
|
||||
VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
|
||||
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED
|
||||
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED
|
||||
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
|
||||
imageMemoryBarrier.subresourceRange.levelCount = t.mips;
|
||||
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
|
||||
imageMemoryBarrier.subresourceRange.layerCount = 1;
|
||||
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
{
|
||||
VkPipelineStageFlags srcStageMask, dstStageMask;
|
||||
imageMemoryBarrier.image = t.image;
|
||||
imageMemoryBarrier.oldLayout = t.imageLayout;
|
||||
imageMemoryBarrier.newLayout = t.imageLayout;
|
||||
|
||||
imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
||||
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
|
||||
vkCmdPipelineBarrier( commandBuffer,
|
||||
srcStageMask, dstStageMask,
|
||||
VK_FLAGS_NONE,
|
||||
0, NULL,
|
||||
0, NULL,
|
||||
1, &imageMemoryBarrier
|
||||
);
|
||||
|
||||
t.imageLayout = imageMemoryBarrier.newLayout;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
});
|
||||
}
|
||||
#endif
|
||||
@ -242,7 +175,12 @@ void ext::VoxelizerBehavior::tick( uf::Object& self ) {
|
||||
}
|
||||
}
|
||||
if ( renderMode.execute ) {
|
||||
pod::Vector3f controllerPosition = controllerTransform.position;
|
||||
pod::Vector3f controllerPosition = controllerTransform.position - metadata.extents.min;
|
||||
controllerPosition.x = floor(controllerPosition.x);
|
||||
controllerPosition.y = floor(controllerPosition.y);
|
||||
controllerPosition.z = floor(controllerPosition.z);
|
||||
controllerPosition += metadata.extents.min;
|
||||
|
||||
controllerPosition.x = floor(controllerPosition.x);
|
||||
controllerPosition.y = floor(controllerPosition.y);
|
||||
controllerPosition.z = -floor(controllerPosition.z);
|
||||
|
||||
@ -508,9 +508,9 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
|
||||
bool down = json["mouse"]["state"].as<std::string>() == "Down";
|
||||
bool clicked = false;
|
||||
if ( down ) {
|
||||
pod::Vector2ui position; {
|
||||
position.x = json["mouse"]["position"]["x"].as<int>() > 0 ? json["mouse"]["position"]["x"].as<size_t>() : 0;
|
||||
position.y = json["mouse"]["position"]["y"].as<int>() > 0 ? json["mouse"]["position"]["y"].as<size_t>() : 0;
|
||||
pod::Vector2ui position = uf::vector::decode( json["mouse"]["position"], pod::Vector2ui{} ); {
|
||||
// position.x = json["mouse"]["position"]["x"].as<int>() > 0 ? json["mouse"]["position"]["x"].as<size_t>() : 0;
|
||||
// position.y = json["mouse"]["position"]["y"].as<int>() > 0 ? json["mouse"]["position"]["y"].as<size_t>() : 0;
|
||||
}
|
||||
pod::Vector2f click; {
|
||||
click.x = (float) position.x / (float) ext::gui::size.current.x;
|
||||
@ -524,8 +524,11 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
|
||||
if (json["invoker"] == "vr" ) {
|
||||
x = json["mouse"]["position"]["x"].as<float>();
|
||||
y = json["mouse"]["position"]["y"].as<float>();
|
||||
pod::Vector3f xy = uf::vector::decode( json["mouse"]["position"], pod::Vector2i{} );
|
||||
x = xy.x;
|
||||
y = xy.y;
|
||||
// x = json["mouse"]["position"]["x"].as<float>();
|
||||
// y = json["mouse"]["position"]["y"].as<float>();
|
||||
}
|
||||
clicked = ( metadata.box.min.x <= x && metadata.box.min.y <= y && metadata.box.max.x >= x && metadata.box.max.y >= y );
|
||||
}
|
||||
@ -570,9 +573,9 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
bool down = json["mouse"]["state"].as<std::string>() == "Down";
|
||||
bool clicked = false;
|
||||
pod::Vector2ui position; {
|
||||
position.x = json["mouse"]["position"]["x"].as<int>() > 0 ? json["mouse"]["position"]["x"].as<size_t>() : 0;
|
||||
position.y = json["mouse"]["position"]["y"].as<int>() > 0 ? json["mouse"]["position"]["y"].as<size_t>() : 0;
|
||||
pod::Vector2ui position = uf::vector::decode( json["mouse"]["position"], pod::Vector2ui{} ); {
|
||||
// position.x = json["mouse"]["position"]["x"].as<int>() > 0 ? json["mouse"]["position"]["x"].as<size_t>() : 0;
|
||||
// position.y = json["mouse"]["position"]["y"].as<int>() > 0 ? json["mouse"]["position"]["y"].as<size_t>() : 0;
|
||||
}
|
||||
pod::Vector2f click; {
|
||||
click.x = (float) position.x / (float) ext::gui::size.current.x;
|
||||
|
||||
@ -48,8 +48,9 @@ void ext::GuiHtmlBehavior::initialize( uf::Object& self ) {
|
||||
size = ext::gui::size.current;
|
||||
}
|
||||
if ( size.x <= 0 && size.y <= 0 ) {
|
||||
size.x = ext::config["window"]["size"]["x"].as<size_t>();
|
||||
size.y = ext::config["window"]["size"]["y"].as<size_t>();
|
||||
// size.x = ext::config["window"]["size"]["x"].as<size_t>();
|
||||
// size.y = ext::config["window"]["size"]["y"].as<size_t>();
|
||||
size = uf::vector::decode( ext::config["window"]["size"], pod::Vector2ui{} );
|
||||
}
|
||||
page = ext::ultralight::create( page, metadata["html"].as<std::string>(), size);
|
||||
std::string onLoad = this->formatHookName("html:Load.%UID%");
|
||||
@ -67,9 +68,9 @@ void ext::GuiHtmlBehavior::initialize( uf::Object& self ) {
|
||||
this->addHook( "window:Resized", [&](ext::json::Value& json){
|
||||
if ( !this->hasComponent<uf::GuiMesh>() ) return;
|
||||
|
||||
pod::Vector2ui size; {
|
||||
size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
pod::Vector2ui size = uf::vector::decode( json["window"]["size"], pod::Vector2ui{} ); {
|
||||
// size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
// size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
};
|
||||
|
||||
metadata["size"][0] = size.x;
|
||||
|
||||
@ -60,9 +60,9 @@ void ext::GuiManagerBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
this->addHook( "window:Resized", [&](ext::json::Value& json){
|
||||
|
||||
pod::Vector2ui size; {
|
||||
size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
pod::Vector2ui size = uf::vector::decode( json["window"]["size"], pod::Vector2ui{} ); {
|
||||
// size.x = json["window"]["size"]["x"].as<size_t>();
|
||||
// size.y = json["window"]["size"]["y"].as<size_t>();
|
||||
}
|
||||
ext::gui::size.current = size;
|
||||
// ext::gui::size.reference = size;
|
||||
@ -71,9 +71,9 @@ void ext::GuiManagerBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
bool down = json["mouse"]["state"].as<std::string>() == "Down";
|
||||
bool clicked = false;
|
||||
pod::Vector2ui position; {
|
||||
position.x = json["mouse"]["position"]["x"].as<int>() > 0 ? json["mouse"]["position"]["x"].as<size_t>() : 0;
|
||||
position.y = json["mouse"]["position"]["y"].as<int>() > 0 ? json["mouse"]["position"]["y"].as<size_t>() : 0;
|
||||
pod::Vector2ui position = uf::vector::decode( json["mouse"]["position"], pod::Vector2ui{} ); {
|
||||
// position.x = json["mouse"]["position"]["x"].as<int>() > 0 ? json["mouse"]["position"]["x"].as<size_t>() : 0;
|
||||
// position.y = json["mouse"]["position"]["y"].as<int>() > 0 ? json["mouse"]["position"]["y"].as<size_t>() : 0;
|
||||
}
|
||||
pod::Vector2f click; {
|
||||
click.x = (float) position.x / (float) ext::gui::size.current.x;
|
||||
|
||||
@ -9,8 +9,9 @@
|
||||
uf::Serializer file;
|
||||
} config;
|
||||
/* Read from file */ if (( file.exists = config.file.readFromFile(file.filename) )) {
|
||||
persistent.window.size.x = config.file["window"]["size"]["x"].as<size_t>();
|
||||
persistent.window.size.y = config.file["window"]["size"]["y"].as<size_t>();
|
||||
persistent.window.size = uf::vector::decode( config.file["window"]["size"], pod::Vector2ui{} );
|
||||
// persistent.window.size.x = config.file["window"]["size"]["x"].as<size_t>();
|
||||
// persistent.window.size.y = config.file["window"]["size"]["y"].as<size_t>();
|
||||
if ( config.file["window"]["title"] != "null" ) {
|
||||
persistent.window.title = config.file["window"]["title"].as<std::string>();
|
||||
}
|
||||
@ -20,8 +21,9 @@
|
||||
std::string hook = "window:Resized";
|
||||
json["type"] = hook;
|
||||
json["invoker"] = "ext";
|
||||
json["window"]["size"]["x"] = persistent.window.size.x;
|
||||
json["window"]["size"]["y"] = persistent.window.size.y;
|
||||
json["window"]["size"] = uf::vector::encode( persistent.window.size );
|
||||
// json["window"]["size"]["x"] = persistent.window.size.x;
|
||||
// json["window"]["size"]["y"] = persistent.window.size.y;
|
||||
if ( persistent.window.size.x != 0 && persistent.window.size.y != 0 )
|
||||
uf::hooks.call(hook, json);
|
||||
}
|
||||
|
||||
@ -104,8 +104,8 @@ void ext::TerrainBehavior::initialize( uf::Object& self ) {
|
||||
if ( _ != "" ) suffix = _ + ".";
|
||||
}
|
||||
graphic.material.initializeShaders({
|
||||
{uf::io::root+"/shaders/terrain/vert.spv", uf::renderer::enums::Shader::VERTEX},
|
||||
{uf::io::root+"/shaders/terrain/frag.spv", uf::renderer::enums::Shader::FRAGMENT}
|
||||
{uf::io::root+"/shaders/terrain/base.vert.spv", uf::renderer::enums::Shader::VERTEX},
|
||||
{uf::io::root+"/shaders/terrain/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT}
|
||||
});
|
||||
// uf::renderer::rebuildOnTickStart = false;
|
||||
}
|
||||
|
||||
@ -54,8 +54,8 @@ void ext::RegionBehavior::initialize( uf::Object& self ) {
|
||||
}
|
||||
|
||||
graphic.material.initializeShaders({
|
||||
{uf::io::root+"/shaders/terrain/vert.spv", uf::renderer::enums::Shader::VERTEX},
|
||||
{uf::io::root+"/shaders/terrain/frag.spv", uf::renderer::enums::Shader::FRAGMENT}
|
||||
{uf::io::root+"/shaders/terrain/base.vert.spv", uf::renderer::enums::Shader::VERTEX},
|
||||
{uf::io::root+"/shaders/terrain/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user