#define M_PI 3.14159265359 #define ONE_BY_PI (1 / 3.14159265359) const vec2 POISSON_DISK_16[16] = vec2[]( vec2(0.3568125,-0.5825516), vec2(-0.2828444,-0.1149732), vec2(-0.2575171,-0.579991), vec2(0.3328768,-0.0916517), vec2(-0.0177952,-0.9652126), vec2(0.7636694,-0.3370355), vec2(0.9381924,0.05975571), vec2(0.6547356,0.373677), vec2(-0.1999273,0.4483816), vec2(0.167026,0.2838214), vec2(0.2164582,0.6978411), vec2(-0.7202712,-0.07400024), vec2(-0.6624036,0.559697), vec2(-0.1909649,0.8721116), vec2(-0.6493049,-0.4945979), vec2(0.6104985,0.7838438) ); struct Probe { vec4 pos; vec4 rot; vec4 inner_range; vec4 outer_range; vec4 sh_coefs[9]; }; struct Light { vec4 pos_radius; vec4 color_attn; }; struct Cluster { int offset; int lights_count; int probes_count; }; layout(std430, binding = 6) readonly buffer lights { Light b_lights[]; }; layout(std430, binding = 7) readonly buffer clusters { Cluster b_clusters[]; }; layout(std430, binding = 8) readonly buffer cluster_maps { int b_cluster_map[]; }; layout(std430, binding = 9) readonly buffer probes { Probe b_probes[]; }; struct PixelData { vec4 albedo; float roughness; float metallic; float emission; vec3 normal; vec3 wpos; } data; float saturate(float a) { return clamp(a, 0, 1); } vec2 saturate(vec2 a) { return clamp(a, vec2(0), vec2(1)); } vec3 saturate(vec3 a) { return clamp(a, vec3(0), vec3(1)); } vec4 saturate(vec4 a) { return clamp(a, vec4(0), vec4(1)); } vec4 fullscreenQuad(int vertexID, out vec2 uv) { uv = vec2((vertexID & 1), (vertexID & 2) * 0.5); #ifdef _ORIGIN_BOTTOM_LEFT return vec4((vertexID & 1) * 2 - 1, (vertexID & 2) - 1, 0, 1); #else return vec4((vertexID & 1) * 2 - 1, -(vertexID & 2) + 1, 0, 1); #endif } float packEmission(float emission) { return log2(1 + emission / 64); } float unpackEmission(float emission) { return (exp2(emission) - 1) * 64; } float shadowmapValue(float frag_z) { return exp(64 / 5000.0 * (frag_z * (u_shadow_far_plane - u_shadow_near_plane ))); } // TODO optimize float toLinearDepth(mat4 inv_proj, float ndc_depth) { vec4 pos_proj = vec4(0, 0, ndc_depth, 1.0); vec4 view_pos = inv_proj * pos_proj; return -view_pos.z / view_pos.w; } #ifdef LUMIX_FRAGMENT_SHADER int getClusterIndex(float ndc_depth, out ivec3 cluster) { ivec2 fragcoord = ivec2(gl_FragCoord.xy); #ifndef _ORIGIN_BOTTOM_LEFT fragcoord.y = u_framebuffer_size.y - fragcoord.y - 1; #endif cluster = ivec3(fragcoord.xy / 64, 0); float linear_depth = toLinearDepth(u_camera_inv_projection, ndc_depth); cluster.z = int(log(linear_depth) * 16 / (log(10000 / 0.1)) - 16 * log(0.1) / log(10000 / 0.1)); ivec2 tiles = (u_framebuffer_size + 63) / 64; cluster.y = tiles.y - 1 - cluster.y; return cluster.x + cluster.y * tiles.x + cluster.z * tiles.x * tiles.y; } #endif vec3 getViewPosition(sampler2D depth_buffer, mat4 inv_view_proj, vec2 tex_coord, out float ndc_depth) { float z = texture(depth_buffer, tex_coord).r; #ifdef _ORIGIN_BOTTOM_LEFT vec4 pos_proj = vec4(vec2(tex_coord.x, tex_coord.y) * 2 - 1, z, 1.0); #else vec4 pos_proj = vec4(vec2(tex_coord.x, 1-tex_coord.y) * 2 - 1, z, 1.0); #endif vec4 view_pos = inv_view_proj * pos_proj; ndc_depth = z; return view_pos.xyz / view_pos.w; } vec3 getViewPosition(sampler2D depth_buffer, mat4 inv_view_proj, vec2 tex_coord) { float z = texture(depth_buffer, tex_coord).r; #ifdef _ORIGIN_BOTTOM_LEFT vec4 pos_proj = vec4(vec2(tex_coord.x, tex_coord.y) * 2 - 1, z, 1.0); #else vec4 pos_proj = vec4(vec2(tex_coord.x, 1-tex_coord.y) * 2 - 1, z, 1.0); #endif vec4 view_pos = inv_view_proj * pos_proj; return view_pos.xyz / view_pos.w; } vec3 getTranslucency(vec3 albedo, float translucency, vec3 V, vec3 L, vec3 N, float shadow) { float w = pow(max(0, dot(-V, L)), 64) * shadow; w += abs(dot(V, N)) * 0.1; w *= max(0.5, dot(-L, N)); w *= max(0.5, dot(N, V)); return vec3(albedo * translucency * w); } float random (vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); } float getShadow(sampler2D shadowmap, vec3 wpos) { #ifdef LUMIX_FRAGMENT_SHADER #if 0// PCF vec4 pos = vec4(wpos, 1); vec2 sm_size = textureSize(shadowmap, 0) * 0.3; float scales[] = float[](1, 0.5, 0.25, 0.125); float offsets[] = float[](2e-5, 1e-5, 1e-4, 2e-5); for (int slice = 0; slice < 4; ++slice) { vec4 sc = u_shadowmap_matrices[slice] * pos; sc = sc / sc.w; if (all(lessThan(sc.xyz, vec3(0.99))) && all(greaterThan(sc.xyz, vec3(0.01)))) { // TODO use texture instead float rnd = random(vec2(gl_FragCoord)); float c = cos(rnd); float s = sin(rnd); mat2 rot = mat2(c, s, -s, c); vec2 sm_uv = vec2(sc.x * 0.25 + slice * 0.25, sc.y); float shadow = 0; float receiver = sc.z; float scale = scales[slice]; float offset = offsets[slice]; for (int j = 0; j < 16; ++j) { vec2 uv = sm_uv + POISSON_DISK_16[j] * rot / sm_size * scale; float occluder = textureLod(shadowmap, uv, 0).r; float receiver = shadowmapValue(sc.z); float m = receiver / occluder; shadow += clamp(1 - (1 - m) * 2048, 0.0, 1.0); } return shadow / 16; } } #else // NO PCF vec4 pos = vec4(wpos, 1); for (int i = 0; i < 4; ++i) { vec4 sc = u_shadowmap_matrices[i] * pos; sc = sc / sc.w; if (all(lessThan(sc.xyz, vec3(0.99))) && all(greaterThan(sc.xyz, vec3(0.01)))) { vec2 sm_uv = vec2(sc.x * 0.25 + i * 0.25, sc.y); float occluder = textureLod(shadowmap, sm_uv, 0).r; float receiver = shadowmapValue(sc.z); float m = receiver / occluder; return clamp(1 - (1 - m) * 1024, 0.0, 1.0); } } #endif #endif return 1; } float D_GGX(float ndoth, float roughness) { float a = roughness * roughness; float a2 = a * a; float f = max(1e-5, (ndoth * ndoth) * (a2 - 1) + 1); return a2 / (f * f * M_PI); } float G_SmithSchlickGGX(float ndotl, float ndotv, float roughness) { float r = roughness + 1.0; float k = (r * r) / 8.0; float l = ndotl / (ndotl * (1.0 - k) + k); float v = ndotv / (ndotv * (1.0 - k) + k); return l * v; } vec3 F_Schlick(float cos_theta, vec3 F0) { return mix(F0, vec3(1), pow(1.0 - cos_theta, 5.0)); } vec3 PBR_ComputeDirectLight(vec3 albedo , vec3 N , vec3 L , vec3 V , vec3 light_color , float roughness , float metallic) { vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic); float ndotv = abs(dot(N, V)) + 1e-5f; vec3 H = normalize(V + L); float ldoth = saturate(dot(L, H)); float ndoth = saturate(dot(N, H)); float ndotl = saturate(dot(N, L)); float hdotv = saturate(dot(H, V)); // D GGX float a = roughness * roughness; float a2 = a * a; float f = max(1e-5, (ndoth * ndoth) * (a2 - 1) + 1); float D = a2 / (f * f * M_PI); // G SmithSchlickGGX float k = max(1e-5, a * 0.5); float l = ndotl / (ndotl * (1.0 - k) + k); float v = ndotv / (ndotv * (1.0 - k) + k); float G = l * v; // F Schlick vec3 F = mix(F0, vec3(1), pow(1.0 - hdotv, 5.0)); vec3 specular = D * G * F / max(1e-5, 4 * ndotv * ndotl); vec3 kD = vec3(1.0) - F; kD *= 1.0 - metallic; return (kD * albedo / M_PI + specular) * light_color * ndotl; } vec3 env_brdf_approx (vec3 F0, float roughness, float NoV) { vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022 ); vec4 c1 = vec4(1, 0.0425, 1.0, -0.04 ); vec4 r = roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; return F0 * AB.x + AB.y; } vec3 PBR_ComputeIndirectDiffuse(vec3 irradiance, vec3 albedo, float metallic, vec3 N, vec3 V) { float ndotv = clamp(dot(N , V), 1e-5f, 1); vec3 F0 = mix(vec3(0.04), albedo, metallic); vec3 F = F_Schlick(ndotv, F0); vec3 kd = mix(vec3(1.0) - F, vec3(0.0), metallic); return kd * albedo * irradiance; } vec3 PBR_ComputeIndirectDiffuse(samplerCube irradiancemap, vec3 albedo, float metallic, vec3 N, vec3 V) { vec3 irradiance = texture(irradiancemap, N).rgb; return PBR_ComputeIndirectDiffuse(irradiance, albedo, metallic, N, V); } vec3 PBR_ComputeIndirectSpecular(samplerCube radiancemap, vec3 albedo, float metallic, float roughness, vec3 N, vec3 V) { float ndotv = clamp(dot(N , V ), 1e-5, 1.0); vec3 F0 = mix(vec3(0.04), albedo, metallic); float lod = roughness * 5; vec3 RV = reflect(-V, N); vec4 radiance_rgbm = textureLod(radiancemap, RV, lod); vec3 radiance = radiance_rgbm.rgb * radiance_rgbm.a * 4; return radiance * env_brdf_approx(F0, roughness, ndotv); } vec3 PBR_ComputeIndirectLight(vec3 albedo, float roughness, float metallic, vec3 N, vec3 V) { // TODO //vec3 diffuse = PBR_ComputeIndirectDiffuse(u_irradiancemap, albedo, metallic, N, V); //vec3 specular = PBR_ComputeIndirectSpecular(u_radiancemap, albedo, metallic, roughness, N, V); return /*diffuse + specular*/ vec3(0); } vec3 rotateByQuat(vec4 rot, vec3 pos) { vec3 uv = cross(rot.xyz, pos); vec3 uuv = cross(rot.xyz, uv); uv *= (2.0 * rot.w); uuv *= 2.0; return pos + uv + uuv; } vec3 pbr(vec3 albedo , float roughness , float metallic , float emission , vec3 N , vec3 V , vec3 L , float shadow , vec3 light_color , float indirect_intensity) { vec3 indirect = PBR_ComputeIndirectLight(albedo, roughness, metallic, N, V); vec3 direct = PBR_ComputeDirectLight(albedo , N , L , V , light_color , roughness , metallic); return + direct * shadow + indirect * indirect_intensity + emission * albedo ; } float rand(vec3 seed) { float dot_product = dot(seed, vec3(12.9898,78.233,45.164)); return fract(sin(dot_product) * 43758.5453); } float getFogFactor(float cam_height , float frag_height , vec3 to_fragment , float fog_density , float fog_bottom , float fog_height) { float fog_top = fog_bottom + fog_height; frag_height = min(frag_height, fog_top); float len = length(to_fragment); vec3 view_dir = to_fragment / len; float y_dir = abs(view_dir.y); cam_height = min(cam_height, fog_top); float avg_y = (frag_height + cam_height) * 0.5; float avg_density = fog_density * clamp(1.0 - (avg_y - fog_bottom) / fog_height, 0, 1); float dist = abs(cam_height - frag_height); if (y_dir <= 0) { dist = len; } else { dist = dist / y_dir; } float res = exp(-pow(avg_density * dist, 2)); return 1 - clamp(res, 0.0, 1.0); } vec3 vegetationAnim(vec3 obj_pos, vec3 vertex_pos) { obj_pos += u_camera_world_pos.xyz; vertex_pos.x += vertex_pos.y > 0.1 ? cos((obj_pos.x + obj_pos.y + obj_pos.z * 2) * 0.3 + u_time * 2) * vertex_pos.y * 0.03 : 0; return vertex_pos; }