From d28155e95249f5d6273c707cb77a10c27b4efcf0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Jun 2020 01:27:57 +0200 Subject: [PATCH] Prefilter envmap stubs --- Shaders/common/prefilter_envmap.frag.glsl | 50 +++++++ Sources/arm/io/ImportEnvmap.hx | 173 +++++++++------------- Sources/arm/io/ImportTexture.hx | 3 +- 3 files changed, 124 insertions(+), 102 deletions(-) create mode 100644 Shaders/common/prefilter_envmap.frag.glsl diff --git a/Shaders/common/prefilter_envmap.frag.glsl b/Shaders/common/prefilter_envmap.frag.glsl new file mode 100644 index 00000000..deeaf276 --- /dev/null +++ b/Shaders/common/prefilter_envmap.frag.glsl @@ -0,0 +1,50 @@ +#version 450 + +in vec2 texCoord; + +uniform sampler2D radiance; +uniform vec4 params; + +out vec4 fragColor; + +const float PI = 3.14159265358979; +const float PI2 = PI * 2.0; +const int samples = 32; + +float rand(const vec2 co) { + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); +} + +vec2 equirect(vec3 normal) { + float phi = acos(normal.z); + float theta = atan(-normal.y, normal.x) + PI; + return vec2(theta / PI2, phi / PI); +} + +vec3 reverseEquirect(vec2 co) { + float theta = co.x * PI2; + float phi = co.y * PI; + return vec3(cos(theta), -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)); + 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 rz = sqrt(1.0 - r.y); + vec3 rr = vec3(rx * uu + ry * vv + rz * n); + return normalize(rr); +} + +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); + fragColor.rgb += texture(radiance, equirect(dir)).rgb; + } + fragColor.rgb /= float(samples); +} diff --git a/Sources/arm/io/ImportEnvmap.hx b/Sources/arm/io/ImportEnvmap.hx index 2209f03d..7bb44673 100644 --- a/Sources/arm/io/ImportEnvmap.hx +++ b/Sources/arm/io/ImportEnvmap.hx @@ -3,125 +3,98 @@ package arm.io; import kha.Image; import kha.Blob; import kha.graphics4.TextureFormat; +import kha.graphics4.PipelineState; +import kha.graphics4.VertexStructure; +import kha.graphics4.VertexData; +import kha.graphics4.TextureUnit; +import kha.graphics4.ConstantLocation; import kha.arrays.Float32Array; import iron.data.Data; +import iron.data.ConstData; import iron.Scene; -import arm.ui.UISidebar; import arm.sys.Path; class ImportEnvmap { + static var pipeline: PipelineState = null; + static var paramsLocation: ConstantLocation; + static var params = new iron.math.Vec4(); + static var radianceLocation: TextureUnit; + static var radiance: Image = null; + static var mips: Array = null; + public static function run(path: String, image: Image) { - var p = Path.data(); - #if krom_windows - var cmft = p + "cmft.exe"; - #elseif krom_linux - var cmft = p + "cmft-linux64"; - #else - var cmft = p + "cmft-osx"; - #end - var tmp = Krom.getFilesLocation() + Path.sep + Data.dataPath; + // Init + if (pipeline == null) { + pipeline = new PipelineState(); + pipeline.vertexShader = Reflect.field(kha.Shaders, "pass_vert"); + pipeline.fragmentShader = Reflect.field(kha.Shaders, "prefilter_envmap_frag"); + var vs = new VertexStructure(); + vs.add("pos", VertexData.Float2); + pipeline.inputLayout = [vs]; + pipeline.compile(); + paramsLocation = pipeline.getConstantLocation("params"); + radianceLocation = pipeline.getTextureUnit("radiance"); - // Irr - var cmd = cmft; - cmd += ' --input "' + path + '"'; - cmd += " --filter shcoeffs"; - cmd += " --outputNum 1"; - cmd += ' --output0 "' + tmp + 'tmp_irr"'; - Krom.sysCommand(cmd); + radiance = Image.createRenderTarget(512, 256, TextureFormat.RGBA64); - // Rad - var faceSize = Std.int(image.width / 8); - cmd = cmft; - cmd += ' --input "' + path + '"'; - cmd += " --filter radiance"; - cmd += " --dstFaceSize " + faceSize; - cmd += " --srcFaceSize " + faceSize; - cmd += " --excludeBase false"; - cmd += " --glossScale 8"; - cmd += " --glossBias 3"; - cmd += " --lightingModel blinnbrdf"; - cmd += " --edgeFixup none"; - cmd += " --numCpuProcessingThreads 4"; - cmd += " --useOpenCL true"; - cmd += " --clVendor anyGpuVendor"; - cmd += " --deviceType gpu"; - cmd += " --deviceIndex 0"; - cmd += " --generateMipChain true"; - cmd += " --inputGammaNumerator 1.0"; - cmd += " --inputGammaDenominator 2.2"; - cmd += " --outputGammaNumerator 1.0"; - cmd += " --outputGammaDenominator 1.0"; - cmd += " --outputNum 1"; - cmd += ' --output0 "' + tmp + 'tmp_rad"'; - cmd += " --output0params hdr,rgbe,latlong"; - Krom.sysCommand(cmd); + mips = []; + var w = 256; + for (i in 0...9) { + mips.push(Image.createRenderTarget(w, w > 1 ? Std.int(w / 2) : 1, TextureFormat.RGBA64)); + w = Std.int(w / 2); + } - // Load irr - Data.getBlob("tmp_irr.c", function(blob: Blob) { - var lines = blob.toString().split("\n"); - var band0 = lines[5]; - var band1 = lines[6]; - var band2 = lines[7]; - band0 = band0.substring(band0.indexOf("{"), band0.length); - band1 = band1.substring(band1.indexOf("{"), band1.length); - band2 = band2.substring(band2.indexOf("{"), band2.length); - var band = band0 + band1 + band2; - band = band.replace("{", ""); - band = band.replace("}", ""); - var ar = band.split(","); - var buf = new Float32Array(28); // Align to mult of 4 - 27->28 - for (i in 0...27) buf[i] = Std.parseFloat(ar[i]); - Scene.active.world.probe.irradiance = buf; - Context.ddirty = 2; - Data.deleteBlob("tmp_irr.c"); - }); + if (ConstData.screenAlignedVB == null) ConstData.createScreenAlignedData(); + } - // World envmap + // Down-scale to 512x256 + radiance.g2.begin(false); + radiance.g2.drawImage(image, 0, 0); + radiance.g2.end(); + + var radianceCpu = Image.fromBytes(radiance.getPixels(), radiance.width, radiance.height, TextureFormat.RGBA64, kha.graphics4.Usage.DynamicUsage); + + // Radiance + var mipsCpu: Array = []; + 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)); + } + radianceCpu.setMipmaps(mipsCpu); + + // Irradiance + Scene.active.world.probe.irradiance = getSphericalHarmonics(radiance); + + // World Scene.active.world.probe.raw.strength = 1.0; Scene.active.world.envmap = image; Scene.active.world.raw.envmap = path; + Scene.active.world.probe.radiance = radianceCpu; + Scene.active.world.probe.radianceMipmaps = mips; Context.savedEnvmap = image; Context.showEnvmapHandle.selected = Context.showEnvmap = true; + if (Context.showEnvmapBlur) { + Scene.active.world.envmap = Scene.active.world.probe.radianceMipmaps[0]; + } + Context.ddirty = 2; + } - // Load envmap clone and set mipmaps - Data.cachedImages.remove(path); - @:privateAccess Data.loadingImages.remove(path); - Data.getImage(path, function(image: kha.Image) { + static function getRadianceMip(mip: kha.Image, level: Int, radiance: kha.Image) { + mip.g4.begin(); + mip.g4.setVertexBuffer(ConstData.screenAlignedVB); + mip.g4.setIndexBuffer(ConstData.screenAlignedIB); + mip.g4.setPipeline(pipeline); + params.x = level; + mip.g4.setFloat4(paramsLocation, params.x, params.y, params.z, params.w); + mip.g4.setTexture(radianceLocation, radiance); + mip.g4.drawIndexedVertices(); + mip.g4.end(); + } - // Load mips - var mipsCount = 6 + Std.int(image.width / 1024); - var mipsLoaded = 0; - var mips: Array = []; - while (mips.length < mipsCount + 2) mips.push(null); - var mw = Std.int(image.width / 2); - var mh = Std.int(image.width / 4); - for (i in 0...mipsCount) { - var radiance_file = "tmp_rad_" + i + "_" + mw + "x" + mh + ".hdr"; - Data.cachedImages.remove(radiance_file); - Data.getImage(radiance_file, function(mip: Image) { - mips[i] = mip; - mipsLoaded++; - if (mipsLoaded == mipsCount) { - // 2x1 and 1x1 mips - var b2x1 = haxe.io.Bytes.alloc(2 * 1 * 4 * 4); // w * h * channels * format_size - var b1x1 = haxe.io.Bytes.alloc(1 * 1 * 4 * 4); - mips[mipsCount] = Image.fromBytes(b2x1, 2, 1, TextureFormat.RGBA128, kha.graphics4.Usage.DynamicUsage); - mips[mipsCount + 1] = Image.fromBytes(b1x1, 1, 1, TextureFormat.RGBA128, kha.graphics4.Usage.DynamicUsage); - // Set radiance - image.setMipmaps(mips); - Scene.active.world.probe.radiance = image; - Scene.active.world.probe.radianceMipmaps = mips; - if (Context.showEnvmapBlur) { - Scene.active.world.envmap = Scene.active.world.probe.radianceMipmaps[0]; - } - Context.ddirty = 2; - } - }, true); // Readable - mw = Std.int(mw / 2); - mh = Std.int(mh / 2); - } - }); + static function getSphericalHarmonics(source: kha.Image) { + var sh = new Float32Array(9 * 3 + 1); // Align to mult of 4 - 27->28 + return sh; } } diff --git a/Sources/arm/io/ImportTexture.hx b/Sources/arm/io/ImportTexture.hx index 0462fcbc..92106139 100644 --- a/Sources/arm/io/ImportTexture.hx +++ b/Sources/arm/io/ImportTexture.hx @@ -32,8 +32,7 @@ class ImportTexture { UISidebar.inst.hwnd2.redraws = 2; // Set envmap - if (path.toLowerCase().endsWith(".hdr") && - (image.width == 1024 || image.width == 2048 || image.width == 4096)) { + if (path.toLowerCase().endsWith(".hdr")) { ImportEnvmap.run(path, image); } });