Prefilter envmap

This commit is contained in:
luboslenco
2020-06-29 19:02:24 +02:00
parent f81c3e12de
commit 9c9b2157ea
4 changed files with 97 additions and 32 deletions
+10 -9
View File
@@ -9,10 +9,10 @@ out vec4 fragColor;
const float PI = 3.14159265358979;
const float PI2 = PI * 2.0;
const int samples = 32;
const int samples = 1024 * 16;
float rand(const vec2 co) {
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
float rand(vec2 co) {
return fract(sin(mod(dot(co.xy, vec2(12.9898, 78.233)), 3.14)) * 43758.5453);
}
vec2 equirect(vec3 normal) {
@@ -22,18 +22,18 @@ vec2 equirect(vec3 normal) {
}
vec3 reverseEquirect(vec2 co) {
float theta = co.x * PI2;
float theta = co.x * PI2 - PI;
float phi = co.y * PI;
return vec3(cos(theta), -sin(theta), cos(phi));
return vec3(sin(phi) * cos(theta), -(sin(phi) * sin(theta)), cos(phi));
}
vec3 cos_weighted_hemisphere_direction(vec3 n, vec2 co, uint seed) {
vec2 r = vec2(rand(co * seed), rand(co * 2 * seed));
vec2 r = vec2(rand(co * seed), rand(co * seed * 2));
vec3 uu = normalize(cross(n, vec3(0.0, 1.0, 1.0)));
vec3 vv = cross(uu, n);
float ra = sqrt(r.y);
float rx = ra * cos(6.2831 * r.x);
float ry = ra * sin(6.2831 * r.x);
float rx = ra * cos(PI2 * r.x);
float ry = ra * sin(PI2 * r.x);
float rz = sqrt(1.0 - r.y);
vec3 rr = vec3(rx * uu + ry * vv + rz * n);
return normalize(rr);
@@ -43,8 +43,9 @@ void main() {
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
vec3 n = reverseEquirect(texCoord);
for (int i = 0; i < samples; i++) {
vec3 dir = mix(n, cos_weighted_hemisphere_direction(n, texCoord, i), params.x / 32.0);
vec3 dir = normalize(mix(n, cos_weighted_hemisphere_direction(n, texCoord, i), params.x));
fragColor.rgb += texture(radiance, equirect(dir)).rgb;
}
fragColor.rgb /= float(samples);
fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2));
}
-8
View File
@@ -22,14 +22,6 @@ float rand(const vec2 co) { // Unreliable
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
}
vec2 rand2(const vec2 coord) {
const float width = 1100;
const float height = 500;
float noiseX = ((fract(1.0 - coord.s * (width / 2.0)) * 0.25) + (fract(coord.t * (height / 2.0)) * 0.75)) * 2.0 - 1.0;
float noiseY = ((fract(1.0 - coord.s * (width / 2.0)) * 0.75) + (fract(coord.t * (height / 2.0)) * 0.25)) * 2.0 - 1.0;
return vec2(noiseX, noiseY);
}
float linearize(const float depth, vec2 cameraProj) {
// to viewz
return cameraProj.y / (depth - cameraProj.x);
+75 -14
View File
@@ -8,9 +8,13 @@ import kha.graphics4.VertexStructure;
import kha.graphics4.VertexData;
import kha.graphics4.TextureUnit;
import kha.graphics4.ConstantLocation;
import kha.graphics4.TextureAddressing;
import kha.graphics4.TextureFilter;
import kha.graphics4.MipMapFilter;
import kha.arrays.Float32Array;
import iron.data.Data;
import iron.data.ConstData;
import iron.math.Vec4;
import iron.Scene;
import arm.sys.Path;
@@ -18,10 +22,13 @@ class ImportEnvmap {
static var pipeline: PipelineState = null;
static var paramsLocation: ConstantLocation;
static var params = new iron.math.Vec4();
static var params = new Vec4();
static var n = new Vec4();
static var radianceLocation: TextureUnit;
static var radiance: Image = null;
static var radianceCpu: Image = null;
static var mips: Array<Image> = null;
static var mipsCpu: Array<Image> = null;
public static function run(path: String, image: Image) {
@@ -33,46 +40,54 @@ class ImportEnvmap {
var vs = new VertexStructure();
vs.add("pos", VertexData.Float2);
pipeline.inputLayout = [vs];
pipeline.colorAttachmentCount = 1;
pipeline.colorAttachments[0] = TextureFormat.RGBA128;
pipeline.compile();
paramsLocation = pipeline.getConstantLocation("params");
radianceLocation = pipeline.getTextureUnit("radiance");
radiance = Image.createRenderTarget(512, 256, TextureFormat.RGBA64);
radiance = Image.createRenderTarget(1024, 512, TextureFormat.RGBA128);
mips = [];
var w = 256;
for (i in 0...9) {
mips.push(Image.createRenderTarget(w, w > 1 ? Std.int(w / 2) : 1, TextureFormat.RGBA64));
var w = 512;
for (i in 0...10) {
mips.push(Image.createRenderTarget(w, w > 1 ? Std.int(w / 2) : 1, TextureFormat.RGBA128));
w = Std.int(w / 2);
}
if (ConstData.screenAlignedVB == null) ConstData.createScreenAlignedData();
}
// Down-scale to 512x256
// Down-scale to 1024x512
radiance.g2.begin(false);
radiance.g2.drawImage(image, 0, 0);
radiance.g2.pipeline = Layers.pipeCopy128;
radiance.g2.drawScaledImage(image, 0, 0, 1024, 512);
radiance.g2.pipeline = null;
radiance.g2.end();
var radianceCpu = Image.fromBytes(radiance.getPixels(), radiance.width, radiance.height, TextureFormat.RGBA64, kha.graphics4.Usage.DynamicUsage);
var radiancePixels = radiance.getPixels();
if (radianceCpu != null) radianceCpu.unload();
radianceCpu = Image.fromBytes(radiancePixels, radiance.width, radiance.height, TextureFormat.RGBA128, kha.graphics4.Usage.DynamicUsage);
// Radiance
var mipsCpu: Array<Image> = [];
if (mipsCpu != null) for (mip in mipsCpu) mip.unload();
mipsCpu = [];
for (i in 0...mips.length) {
getRadianceMip(mips[i], i, radiance);
mipsCpu.push(Image.fromBytes(mips[i].getPixels(), mips[i].width, mips[i].height, TextureFormat.RGBA64, kha.graphics4.Usage.DynamicUsage));
mipsCpu.push(Image.fromBytes(mips[i].getPixels(), mips[i].width, mips[i].height, TextureFormat.RGBA128, kha.graphics4.Usage.DynamicUsage));
}
radianceCpu.setMipmaps(mipsCpu);
// Irradiance
Scene.active.world.probe.irradiance = getSphericalHarmonics(radiance);
Scene.active.world.probe.irradiance = getSphericalHarmonics(radiancePixels, radiance.width, radiance.height);
// World
Scene.active.world.probe.raw.strength = 1.0;
Scene.active.world.probe.raw.radiance_mipmaps = mipsCpu.length - 2;
Scene.active.world.envmap = image;
Scene.active.world.raw.envmap = path;
Scene.active.world.probe.radiance = radianceCpu;
Scene.active.world.probe.radianceMipmaps = mips;
Scene.active.world.probe.radianceMipmaps = mipsCpu;
Context.savedEnvmap = image;
Context.showEnvmapHandle.selected = Context.showEnvmap = true;
if (Context.showEnvmapBlur) {
@@ -86,15 +101,61 @@ class ImportEnvmap {
mip.g4.setVertexBuffer(ConstData.screenAlignedVB);
mip.g4.setIndexBuffer(ConstData.screenAlignedIB);
mip.g4.setPipeline(pipeline);
params.x = level;
params.x = 0.1 + level / 8;
mip.g4.setFloat4(paramsLocation, params.x, params.y, params.z, params.w);
mip.g4.setTexture(radianceLocation, radiance);
mip.g4.drawIndexedVertices();
mip.g4.end();
}
static function getSphericalHarmonics(source: kha.Image) {
static function reverseEquirect(x: Float, y: Float): Vec4 {
var theta = x * Math.PI * 2 - Math.PI;
var phi = y * Math.PI;
// return n.set(Math.sin(phi) * Math.cos(theta), -(Math.sin(phi) * Math.sin(theta)), Math.cos(phi));
return n.set(-Math.cos(phi), Math.sin(phi) * Math.cos(theta), -(Math.sin(phi) * Math.sin(theta)));
}
// https://ndotl.wordpress.com/2015/03/07/pbr-cubemap-filtering
// https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering
static function getSphericalHarmonics(source: haxe.io.Bytes, sourceWidth: Int, sourceHeight: Int): Float32Array {
var sh = new Float32Array(9 * 3 + 1); // Align to mult of 4 - 27->28
var accum = 0.0;
var weight = 1.0;
var weight1 = weight * 4 / 17;
var weight2 = weight * 8 / 17;
var weight3 = weight * 15 / 17;
var weight4 = weight * 5 / 68;
var weight5 = weight * 15 / 68;
for (x in 0...sourceWidth) {
for (y in 0...sourceHeight) {
n = reverseEquirect(x / sourceWidth, y / sourceHeight);
for (i in 0...3) {
var value = source.getFloat(((x + y * sourceWidth) * 16 + i * 4));
value = Math.pow(value, 1.0 / 2.2);
sh[0 + i] += value * weight1;
sh[3 + i] += value * weight2 * n.x;
sh[6 + i] += value * weight2 * n.y;
sh[9 + i] += value * weight2 * n.z;
sh[12 + i] += value * weight3 * n.x * n.z;
sh[15 + i] += value * weight3 * n.z * n.y;
sh[18 + i] += value * weight3 * n.y * n.x;
sh[21 + i] += value * weight4 * (3.0 * n.z * n.z - 1.0);
sh[24 + i] += value * weight5 * (n.x * n.x - n.y * n.y);
accum += weight;
}
}
}
for (i in 0...sh.length) {
sh[i] /= accum / 16;
}
return sh;
}
}
+12 -1
View File
@@ -14,7 +14,18 @@ class ImportTexture {
return;
}
for (a in Project.assets) if (a.file == path) { Log.info(Strings.info0); return; }
for (a in Project.assets) {
if (a.file == path) {
// Set envmap
if (path.toLowerCase().endsWith(".hdr")) {
Data.getImage(path, function(image: kha.Image) {
ImportEnvmap.run(path, image);
});
}
Log.info(Strings.info0);
return;
}
}
var ext = path.substr(path.lastIndexOf(".") + 1);
var importer = Path.textureImporters.get(ext);