You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/next_gen_ui_demo/assets/shaders/orb_shader.frag

333 lines
8.7 KiB

// Copyright 2023 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#version 460 core
#include "common/common.glsl"
#include <flutter/runtime_effect.glsl>
#define RAY_STEPS 30
uniform vec2 uResolution;
uniform vec4 uPackedData;
float uTime = uPackedData[0];
float uExposure = uPackedData[1];
float uFov = uPackedData[2];
float uRoughness = uPackedData[3];
uniform float uMetalness;
uniform vec3 uLightDir;
uniform float uLightR;
uniform vec3 uLightLumP;
uniform vec3 uAlbedo;
uniform float uIor;
uniform float uLightQuadAtt;
uniform vec3 uAmbientLight;
uniform float uAmbientLightDepthFactor;
uniform float uEnergy;
out vec4 oColor;
float noise_2d(vec2 pos) {
vec2 g = floor(pos);
float a = hash_2d(g);
float b = hash_2d(g + vec2(1.0, 0.0));
float c = hash_2d(g + vec2(0.0, 1.0));
float d = hash_2d(g + vec2(1.0, 1.0));
vec2 fp = pos - g;
vec2 sfp = smoothstep(vec2(0.0), vec2(1.0), fp);
return a + (b - a) * sfp.x + (c - a) * sfp.y +
(a - b - c + d) * sfp.x * sfp.y;
}
vec3 closest_point_on_disc(vec3 center, vec3 normal, float radius, vec3 p) {
vec3 r = p - center;
vec3 pr = r - dot(r, normal) * normal;
return center + normalize(pr) * min(length(pr), radius);
}
// Compute area light illuminance from: Moving Frostbite to Physically Based
// Rendering 3.0, Siggraph 2014
float illuminanceSphereOrDisk(float cosTheta, float sinSigmaSqr) {
float cosThetaSqr = cosTheta * cosTheta;
float sinTheta = sqrt(1.0 - cosThetaSqr);
float illuminance = 0.0;
if (cosThetaSqr > sinSigmaSqr) {
illuminance = M_PI * sinSigmaSqr * clamp(cosTheta, 0.0, 1.0);
} else {
float x = sqrt(1.0 / sinSigmaSqr - 1.0);
float y = -x * (cosTheta / sinTheta);
float sinThetaSqrtY = sinTheta * sqrt(1.0 - y * y);
illuminance = (cosTheta * acos(y) - x * sinThetaSqrtY) * sinSigmaSqr +
atan(sinThetaSqrtY / x);
}
return max(illuminance, 0.0);
}
float evalIlluminanceDisk(vec3 N, vec3 L, vec3 lightN, float lightRadius,
float lightDistSqr) {
float cosTheta = dot(N, L);
float lightRSqr = lightRadius * lightRadius;
float sinSigmaSqr = lightRSqr / (lightRSqr + max(lightRSqr, lightDistSqr));
float illuminance = illuminanceSphereOrDisk(cosTheta, sinSigmaSqr) *
clamp(dot(lightN, -L), 0.0, 1.0);
return illuminance;
}
float distribution_ggx(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 nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = M_PI * denom * denom;
return nom / denom;
}
float geometry_schlick_ggx(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float geometry_smith(vec3 N, vec3 V, float cosTheta, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float ggx2 = geometry_schlick_ggx(NdotV, roughness);
float ggx1 = geometry_schlick_ggx(cosTheta, roughness);
return ggx1 * ggx2;
}
vec3 fresnel_schlick(float cosTheta, vec3 F0) {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 brdf_eval(vec3 N, vec3 L, vec3 H, vec3 V, vec3 albedo, float roughness,
float metalness, vec3 Li) {
roughness = max(0.2, roughness);
float cosTheta = max(dot(N, L), 0.0);
// Diffuse color
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metalness);
float NDF = distribution_ggx(N, H, roughness);
float G = geometry_smith(N, V, cosTheta, roughness);
vec3 F = fresnel_schlick(max(dot(H, V), 0.0), F0);
vec3 num = NDF * G * F;
float denom = 4.0 * max(dot(N, V), 0.0) * cosTheta;
vec3 spec = num / max(denom, 0.001);
vec3 kS = F;
vec3 kD = 1.0 - kS;
kD *= (1.0 - metalness);
vec3 Lo = (kD * albedo / M_PI + spec) * Li * cosTheta;
return Lo;
}
vec3 btdf_eval(vec3 N, vec3 L, vec3 albedo, vec3 Li) {
vec3 Lo = albedo * Li * max(dot(N, L), 0.0);
return Lo;
}
vec2 oct_encode(vec3 d) {
vec3 octant = sign(d);
// Compute l1-norm version of the direction vector
float sum = dot(d, octant);
vec3 octahedron = d / sum;
if (octahedron.z < 0.0) {
vec3 a = abs(octahedron);
octahedron.xy = octant.xy * (vec2(1.0) - a.yx);
}
return octahedron.xy * 0.5 + 0.5;
}
const mat2 octM0 = mat2(1.0, 0.0, 0.0, 1.0);
const mat2 octM1 = mat2(0.809017, 0.587785, -0.587785, 0.809017);
const mat2 octM2 = mat2(0.309017, 0.951057, -0.951057, 0.309017);
const mat2 octM3 = mat2(-0.309017, 0.951057, -0.951057, -0.309017);
float fbm(vec2 pos) {
float sum = 0.0;
sum += noise_2d(octM0 * pos);
sum += 0.5 * noise_2d(2.0 * octM1 * pos);
sum += 0.25 * noise_2d(4.0 * octM2 * pos);
sum += 0.125 * noise_2d(8.0 * octM3 * pos);
return sum;
}
vec2 fbm_sphere_sdf(vec3 center, float radius, vec3 pos) {
vec3 toP = pos - center;
radius = radius * mix(0.5, 1.0, uEnergy);
float d = length(toP);
vec2 uv = oct_encode(toP);
float amp = mix(0.1, 0.6, uEnergy);
float dd = fbm(uv * 15.0 + uTime * vec2(1.0, -0.4)) * amp;
return vec2(d + dd - radius,
mix(0.4, 1.0, float(d - (radius + 1.0 * amp) > 0.0)));
}
vec2 sample_scene(vec3 pos) {
return fbm_sphere_sdf(vec3(0.0, 0.0, -10.0), 2.0, pos);
}
const float sampleScale = 1.0 / sqrt(3.0) * 0.0005;
vec3 sample_normal(vec3 pos) {
#define NORMAL_SDF_SAMPLE_COUNT 4
vec3 normalSampleOffsets[NORMAL_SDF_SAMPLE_COUNT];
normalSampleOffsets[0] = vec3(1.0, -1.0, -1.0);
normalSampleOffsets[1] = vec3(-1.0, -1.0, 1.0);
normalSampleOffsets[2] = vec3(-1.0, 1.0, -1.0);
normalSampleOffsets[3] = vec3(1.0, 1.0, 1.0);
vec3 result = vec3(0.0);
for (int i = 0; i < NORMAL_SDF_SAMPLE_COUNT; ++i) {
result += normalSampleOffsets[i] * sampleScale *
sample_scene(pos + normalSampleOffsets[i]).x;
}
return normalize(result);
}
float raymarch(vec3 start, vec3 dir) {
float tMin = 8.0;
float tMax = 15.0;
float t = tMin;
float result = -1.0;
for (int i = 0; i < RAY_STEPS; ++i) {
if (t >= tMax)
break;
vec2 d = sample_scene(start + dir * t);
if (d.x < 0.0002) {
result = t;
break;
}
t += d.x * d.y;
}
return result;
}
void look_at(out mat3 cam, in vec3 eye, in vec3 center, in vec3 up) {
// Construct an ortho-normal basis for the camera
vec3 forward = normalize(center - eye);
vec3 right = cross(forward, up);
up = cross(right, forward);
cam = mat3(right, up, forward);
}
void sample_camera_ray(out vec3 origin, out vec3 direction, in mat3 cam,
vec3 eye, vec2 uv) {
uv *= 2.0;
uv -= 1.0;
uv.y *= -1.0;
float aspectRatio = uResolution.y / uResolution.x;
float vWidth = tan(uFov / 2.0);
float vHeight = vWidth * aspectRatio;
vec3 forward = cam * vec3(0.0, 0.0, 1.0);
vec3 rayDir = cam * vec3(uv.x * vWidth, uv.y * vHeight, 1.0);
origin = eye;
direction = normalize(rayDir);
}
vec3 sample_disk_light(out vec3 L, vec3 P, vec3 N, vec3 lightP, vec3 lightN,
float lightR, vec3 lightAtt, vec3 lightLumP) {
vec3 toL = closest_point_on_disc(lightP, lightN, lightR, P) - P;
L = normalize(toL);
toL *= lightAtt.x;
float illuminance = evalIlluminanceDisk(N, L, lightN, lightR, dot(toL, toL));
vec3 Li = lightLumP * illuminance;
return Li;
}
vec4 pixel_color(vec3 o, vec3 d, vec3 lightP, vec3 lightN, float lightR,
vec3 lightAtt, vec3 lightLumP) {
float t = raymarch(o, d);
vec4 result = vec4(0.0);
if (t >= 0.0) {
vec3 P = o + d * t;
vec3 N = sample_normal(P);
vec3 V = -d;
vec3 R = refract(-V, N, uIor);
float z = dot(vec3(0.0, 0.0, -1.0), P);
float zd = smoothstep(mix(0.0, 11.0, uAmbientLightDepthFactor), 14.0, z);
vec3 Lo = vec3(0.0);
vec3 L, Li;
vec3 S = fresnel_schlick(max(dot(N, V), 0.0), vec3(0.02));
Li =
sample_disk_light(L, P, R, lightP, lightN, lightR, lightAtt, lightLumP);
Lo += (1.0 - S) * brdf_eval(-N, L, normalize(L + R), R, uAlbedo, uRoughness,
uMetalness, Li);
Lo += zd * uAlbedo * uAmbientLight;
result = vec4(Lo, 1.0);
}
return result;
}
void main() {
vec2 uv = vec2(FlutterFragCoord().xy) / uResolution;
vec3 lightN = normalize(-uLightDir);
vec3 lightP = vec3(0.0, 0.0, -10.0) + uLightDir;
vec3 lightAtt = vec3(uLightQuadAtt, 0.0, 1.0);
vec3 eye = vec3(0.0, 0.0, 1.0);
vec3 center = vec3(0.0, 0.0, 0.0);
vec3 up = vec3(0.0, 1.0, 0.0);
mat3 cam;
look_at(cam, eye, center, up);
vec3 o, d;
sample_camera_ray(o, d, cam, eye, uv);
vec4 hdrColor =
abs(pixel_color(o, d, lightP, lightN, uLightR, lightAtt, uLightLumP));
vec3 ldrColor = vec3(1.0) - exp(min(-(hdrColor.rgb) * uExposure, 0.0));
oColor = vec4(ldrColor, hdrColor.a);
}