Files
armorpaint/Sources/arm/io/ImportEnvmap.hx
T

162 lines
5.3 KiB
Haxe
Raw Normal View History

2019-06-19 18:45:36 +02:00
package arm.io;
2019-06-20 11:23:01 +02:00
import kha.Image;
import kha.Blob;
import kha.graphics4.TextureFormat;
2020-06-16 01:27:57 +02:00
import kha.graphics4.PipelineState;
import kha.graphics4.VertexStructure;
import kha.graphics4.VertexData;
import kha.graphics4.TextureUnit;
import kha.graphics4.ConstantLocation;
2020-06-29 19:02:24 +02:00
import kha.graphics4.TextureAddressing;
import kha.graphics4.TextureFilter;
import kha.graphics4.MipMapFilter;
2019-06-20 11:23:01 +02:00
import kha.arrays.Float32Array;
import iron.data.Data;
2020-06-16 01:27:57 +02:00
import iron.data.ConstData;
2020-06-29 19:02:24 +02:00
import iron.math.Vec4;
2019-06-20 11:23:01 +02:00
import iron.Scene;
2020-02-16 21:50:14 +01:00
import arm.sys.Path;
2019-06-19 18:45:36 +02:00
class ImportEnvmap {
2020-06-16 01:27:57 +02:00
static var pipeline: PipelineState = null;
static var paramsLocation: ConstantLocation;
2020-06-29 19:02:24 +02:00
static var params = new Vec4();
static var n = new Vec4();
2020-06-16 01:27:57 +02:00
static var radianceLocation: TextureUnit;
static var radiance: Image = null;
2020-06-29 19:02:24 +02:00
static var radianceCpu: Image = null;
2020-06-16 01:27:57 +02:00
static var mips: Array<Image> = null;
2020-06-29 19:02:24 +02:00
static var mipsCpu: Array<Image> = null;
2020-06-16 01:27:57 +02:00
2019-12-09 00:28:10 +01:00
public static function run(path: String, image: Image) {
2019-06-19 18:45:36 +02:00
2020-06-16 01:27:57 +02:00
// 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];
2020-06-29 19:02:24 +02:00
pipeline.colorAttachmentCount = 1;
pipeline.colorAttachments[0] = TextureFormat.RGBA128;
2020-06-16 01:27:57 +02:00
pipeline.compile();
paramsLocation = pipeline.getConstantLocation("params");
radianceLocation = pipeline.getTextureUnit("radiance");
2020-06-29 19:02:24 +02:00
radiance = Image.createRenderTarget(1024, 512, TextureFormat.RGBA128);
2020-06-16 01:27:57 +02:00
mips = [];
2020-06-29 19:02:24 +02:00
var w = 512;
for (i in 0...10) {
mips.push(Image.createRenderTarget(w, w > 1 ? Std.int(w / 2) : 1, TextureFormat.RGBA128));
2020-06-16 01:27:57 +02:00
w = Std.int(w / 2);
}
if (ConstData.screenAlignedVB == null) ConstData.createScreenAlignedData();
}
2019-06-19 18:45:36 +02:00
2020-06-29 19:02:24 +02:00
// Down-scale to 1024x512
2020-06-16 01:27:57 +02:00
radiance.g2.begin(false);
2020-06-29 19:02:24 +02:00
radiance.g2.pipeline = Layers.pipeCopy128;
radiance.g2.drawScaledImage(image, 0, 0, 1024, 512);
radiance.g2.pipeline = null;
2020-06-16 01:27:57 +02:00
radiance.g2.end();
2019-06-19 18:45:36 +02:00
2020-06-29 19:02:24 +02:00
var radiancePixels = radiance.getPixels();
if (radianceCpu != null) radianceCpu.unload();
radianceCpu = Image.fromBytes(radiancePixels, radiance.width, radiance.height, TextureFormat.RGBA128, kha.graphics4.Usage.DynamicUsage);
2019-06-19 18:45:36 +02:00
2020-06-16 01:27:57 +02:00
// Radiance
2020-06-29 19:02:24 +02:00
if (mipsCpu != null) for (mip in mipsCpu) mip.unload();
mipsCpu = [];
2020-06-16 01:27:57 +02:00
for (i in 0...mips.length) {
getRadianceMip(mips[i], i, radiance);
2020-06-29 19:02:24 +02:00
mipsCpu.push(Image.fromBytes(mips[i].getPixels(), mips[i].width, mips[i].height, TextureFormat.RGBA128, kha.graphics4.Usage.DynamicUsage));
2020-06-16 01:27:57 +02:00
}
radianceCpu.setMipmaps(mipsCpu);
2019-06-19 18:45:36 +02:00
2020-06-16 01:27:57 +02:00
// Irradiance
2020-06-29 19:02:24 +02:00
Scene.active.world.probe.irradiance = getSphericalHarmonics(radiancePixels, radiance.width, radiance.height);
2020-06-16 01:27:57 +02:00
// World
2019-09-15 20:52:34 +02:00
Scene.active.world.probe.raw.strength = 1.0;
2020-06-29 19:02:24 +02:00
Scene.active.world.probe.raw.radiance_mipmaps = mipsCpu.length - 2;
2019-06-20 11:23:01 +02:00
Scene.active.world.envmap = image;
2019-10-22 12:05:39 +02:00
Scene.active.world.raw.envmap = path;
2020-06-16 01:27:57 +02:00
Scene.active.world.probe.radiance = radianceCpu;
2020-06-29 19:02:24 +02:00
Scene.active.world.probe.radianceMipmaps = mipsCpu;
2020-03-26 15:22:22 +01:00
Context.savedEnvmap = image;
Context.showEnvmapHandle.selected = Context.showEnvmap = true;
2020-06-16 01:27:57 +02:00
if (Context.showEnvmapBlur) {
Scene.active.world.envmap = Scene.active.world.probe.radianceMipmaps[0];
}
Context.ddirty = 2;
}
2019-06-19 18:45:36 +02:00
2020-06-16 01:27:57 +02:00
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);
2020-06-29 19:02:24 +02:00
params.x = 0.1 + level / 8;
2020-06-16 01:27:57 +02:00
mip.g4.setFloat4(paramsLocation, params.x, params.y, params.z, params.w);
mip.g4.setTexture(radianceLocation, radiance);
mip.g4.drawIndexedVertices();
mip.g4.end();
}
2019-09-15 20:52:34 +02:00
2020-06-29 19:02:24 +02:00
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 {
2020-06-16 01:27:57 +02:00
var sh = new Float32Array(9 * 3 + 1); // Align to mult of 4 - 27->28
2020-06-29 19:02:24 +02:00
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;
}
2020-06-16 01:27:57 +02:00
return sh;
2019-06-19 18:45:36 +02:00
}
}