Commit for 2021.05.16.7z

This commit is contained in:
mrq 2021-05-16 00:00:00 -05:00
parent 7670c8c3af
commit e7da8db000
89 changed files with 2496 additions and 3856 deletions

View File

@ -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)

View File

@ -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 );

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View 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
}

View 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;
}

View 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;

View 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;
}
}

View 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;
}

View 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;
};

View 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
}

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inUv;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(fragment)
#extension GL_EXT_samplerless_texture_functions : require
layout (binding = 1) uniform sampler samp;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inUv;

View 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));
}

View File

@ -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;

View 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;
}

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inUv;

View File

@ -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();
}

View 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;
}

View 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();
}

View File

@ -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);
}

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inUv;

View File

@ -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();
}

View 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();
}

View File

@ -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);

View File

@ -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 );

View File

@ -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 );

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -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);

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -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
}

View File

@ -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();
}

View File

@ -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;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec2 inPos;

View File

@ -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;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec2 inPos;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(fragment)
layout (location = 0) in vec3 inColor;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -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);
}

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(compute)
layout (local_size_x = 32, local_size_y = 32) in;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(fragment)
layout (binding = 1) uniform sampler2D samplerColor;

View File

@ -1,4 +1,5 @@
#version 450
#pragma shader_stage(vertex)
layout (constant_id = 0) const uint PASSES = 6;
layout (location = 0) in vec3 inPos;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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() {

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View 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 );
};
}
}

View File

@ -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;

View File

@ -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& );

View File

View 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 "";

View File

@ -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 );

View File

@ -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>() ) {

View File

@ -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;

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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 );

View File

@ -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();
}
}

View File

@ -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() {

View File

@ -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;

View 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

View File

@ -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();
}

View File

@ -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();

View File

@ -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;

View File

@ -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";

View File

@ -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 ) );
}

View File

@ -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
}

View File

@ -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";

View File

@ -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();

View File

@ -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>();

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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}
});
}