⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 imagebasedlight.java

📁 Sunflow是一个照片级的渲染系统
💻 JAVA
字号:
package org.sunflow.core.light;

import org.sunflow.SunflowAPI;
import org.sunflow.core.IntersectionState;
import org.sunflow.core.LightSample;
import org.sunflow.core.LightSource;
import org.sunflow.core.ParameterList;
import org.sunflow.core.PrimitiveList;
import org.sunflow.core.Ray;
import org.sunflow.core.Shader;
import org.sunflow.core.ShadingState;
import org.sunflow.core.Texture;
import org.sunflow.core.TextureCache;
import org.sunflow.image.Bitmap;
import org.sunflow.image.Color;
import org.sunflow.math.BoundingBox;
import org.sunflow.math.Matrix4;
import org.sunflow.math.OrthoNormalBasis;
import org.sunflow.math.Point3;
import org.sunflow.math.QMC;
import org.sunflow.math.Vector3;

public class ImageBasedLight implements PrimitiveList, LightSource, Shader {
    private Texture texture;
    private OrthoNormalBasis basis;
    private int numSamples;
    private float jacobian;
    private float[] colHistogram;
    private float[][] imageHistogram;
    private Vector3[] samples;
    private Color[] colors;

    public ImageBasedLight() {
        texture = null;
        updateBasis(new Vector3(0, 0, -1), new Vector3(0, 1, 0));
        numSamples = 64;
    }

    private void updateBasis(Vector3 center, Vector3 up) {
        if (center != null && up != null) {
            basis = OrthoNormalBasis.makeFromWV(center, up);
            basis.swapWU();
            basis.flipV();
        }
    }

    public boolean update(ParameterList pl, SunflowAPI api) {
        updateBasis(pl.getVector("center", null), pl.getVector("up", null));
        numSamples = pl.getInt("samples", numSamples);

        String filename = pl.getString("texture", null);
        if (filename != null)
            texture = TextureCache.getTexture(api.resolveTextureFilename(filename), true);

        // no texture provided
        if (texture == null)
            return false;
        Bitmap b = texture.getBitmap();
        if (b == null)
            return false;

        // rebuild histograms if this is a new texture
        if (filename != null) {
            imageHistogram = new float[b.getWidth()][b.getHeight()];
            colHistogram = new float[b.getWidth()];
            float du = 1.0f / b.getWidth();
            float dv = 1.0f / b.getHeight();
            for (int x = 0; x < b.getWidth(); x++) {
                for (int y = 0; y < b.getHeight(); y++) {
                    float u = (x + 0.5f) * du;
                    float v = (y + 0.5f) * dv;
                    Color c = texture.getPixel(u, v);
                    // box filter the image
                    // c.add(texture.getPixel(u + du, v));
                    // c.add(texture.getPixel(u + du, v+ dv));
                    // c.add(texture.getPixel(u, v + dv));
                    // c.mul(0.25f);
                    imageHistogram[x][y] = c.getLuminance() * (float) Math.sin(Math.PI * v);
                    if (y > 0)
                        imageHistogram[x][y] += imageHistogram[x][y - 1];
                }
                colHistogram[x] = imageHistogram[x][b.getHeight() - 1];
                if (x > 0)
                    colHistogram[x] += colHistogram[x - 1];
                for (int y = 0; y < b.getHeight(); y++)
                    imageHistogram[x][y] /= imageHistogram[x][b.getHeight() - 1];
            }
            for (int x = 0; x < b.getWidth(); x++)
                colHistogram[x] /= colHistogram[b.getWidth() - 1];
            jacobian = (float) (2 * Math.PI * Math.PI) / (b.getWidth() * b.getHeight());
        }
        // take fixed samples
        if (pl.getBoolean("fixed", samples != null)) {
            // Bitmap loc = new Bitmap(filename);
            samples = new Vector3[numSamples];
            colors = new Color[numSamples];
            for (int i = 0; i < numSamples; i++) {
                double randX = (double) i / (double) numSamples;
                double randY = QMC.halton(0, i);
                int x = 0;
                while (randX >= colHistogram[x] && x < colHistogram.length - 1)
                    x++;
                float[] rowHistogram = imageHistogram[x];
                int y = 0;
                while (randY >= rowHistogram[y] && y < rowHistogram.length - 1)
                    y++;
                // sample from (x, y)
                float u = (float) ((x == 0) ? (randX / colHistogram[0]) : ((randX - colHistogram[x - 1]) / (colHistogram[x] - colHistogram[x - 1])));
                float v = (float) ((y == 0) ? (randY / rowHistogram[0]) : ((randY - rowHistogram[y - 1]) / (rowHistogram[y] - rowHistogram[y - 1])));

                float px = ((x == 0) ? colHistogram[0] : (colHistogram[x] - colHistogram[x - 1]));
                float py = ((y == 0) ? rowHistogram[0] : (rowHistogram[y] - rowHistogram[y - 1]));

                float su = (x + u) / colHistogram.length;
                float sv = (y + v) / rowHistogram.length;

                float invP = (float) Math.sin(sv * Math.PI) * jacobian / (numSamples * px * py);
                samples[i] = getDirection(su, sv);
                basis.transform(samples[i]);
                colors[i] = texture.getPixel(su, sv).mul(invP);
                // loc.setPixel(x, y, Color.YELLOW.copy().mul(1e6f));
            }
            // loc.save("samples.hdr");
        } else {
            // turn off
            samples = null;
            colors = null;
        }
        return true;
    }

    public void init(String name, SunflowAPI api) {
        // register this object with the api properly
        api.geometry(name, this);
        if (api.lookupGeometry(name) == null) {
            // quit if we don't see our geometry in here (error message
            // will have already been printed)
            return;
        }
        api.shader(name + ".shader", this);
        api.parameter("shaders", name + ".shader");
        api.instance(name + ".instance", name);
        api.light(name + ".light", this);
    }

    public void prepareShadingState(ShadingState state) {
        if (state.includeLights())
            state.setShader(this);
    }

    public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
        if (r.getMax() == Float.POSITIVE_INFINITY)
            state.setIntersection(0, 0, 0);
    }

    public int getNumPrimitives() {
        return 1;
    }

    public float getPrimitiveBound(int primID, int i) {
        return 0;
    }

    public BoundingBox getWorldBounds(Matrix4 o2w) {
        return null;
    }

    public PrimitiveList getBakingPrimitives() {
        return null;
    }

    public int getNumSamples() {
        return numSamples;
    }

    public void getSamples(ShadingState state) {
        if (samples == null) {
            int n = state.getDiffuseDepth() > 0 ? 1 : numSamples;
            for (int i = 0; i < n; i++) {
                // random offset on unit square, we use the infinite version of
                // getRandom because the light sampling is adaptive
                double randX = state.getRandom(i, 0, n);
                double randY = state.getRandom(i, 1, n);
                int x = 0;
                while (randX >= colHistogram[x] && x < colHistogram.length - 1)
                    x++;
                float[] rowHistogram = imageHistogram[x];
                int y = 0;
                while (randY >= rowHistogram[y] && y < rowHistogram.length - 1)
                    y++;
                // sample from (x, y)
                float u = (float) ((x == 0) ? (randX / colHistogram[0]) : ((randX - colHistogram[x - 1]) / (colHistogram[x] - colHistogram[x - 1])));
                float v = (float) ((y == 0) ? (randY / rowHistogram[0]) : ((randY - rowHistogram[y - 1]) / (rowHistogram[y] - rowHistogram[y - 1])));

                float px = ((x == 0) ? colHistogram[0] : (colHistogram[x] - colHistogram[x - 1]));
                float py = ((y == 0) ? rowHistogram[0] : (rowHistogram[y] - rowHistogram[y - 1]));

                float su = (x + u) / colHistogram.length;
                float sv = (y + v) / rowHistogram.length;
                float invP = (float) Math.sin(sv * Math.PI) * jacobian / (n * px * py);
                Vector3 dir = getDirection(su, sv);
                basis.transform(dir);
                if (Vector3.dot(dir, state.getGeoNormal()) > 0) {
                    LightSample dest = new LightSample();
                    dest.setShadowRay(new Ray(state.getPoint(), dir));
                    dest.getShadowRay().setMax(Float.MAX_VALUE);
                    Color radiance = texture.getPixel(su, sv);
                    dest.setRadiance(radiance, radiance);
                    dest.getDiffuseRadiance().mul(invP);
                    dest.getSpecularRadiance().mul(invP);
                    dest.traceShadow(state);
                    state.addSample(dest);
                }
            }
        } else {
            for (int i = 0; i < numSamples; i++) {
                if (Vector3.dot(samples[i], state.getGeoNormal()) > 0 && Vector3.dot(samples[i], state.getNormal()) > 0) {
                    LightSample dest = new LightSample();
                    dest.setShadowRay(new Ray(state.getPoint(), samples[i]));
                    dest.getShadowRay().setMax(Float.MAX_VALUE);
                    dest.setRadiance(colors[i], colors[i]);
                    dest.traceShadow(state);
                    state.addSample(dest);
                }
            }
        }
    }

    public void getPhoton(double randX1, double randY1, double randX2, double randY2, Point3 p, Vector3 dir, Color power) {
    }

    public Color getRadiance(ShadingState state) {
        // lookup texture based on ray direction
        return state.includeLights() ? getColor(basis.untransform(state.getRay().getDirection(), new Vector3())) : Color.BLACK;
    }

    private Color getColor(Vector3 dir) {
        float u, v;
        // assume lon/lat format
        double phi = 0, theta = 0;
        phi = Math.acos(dir.y);
        theta = Math.atan2(dir.z, dir.x);
        u = (float) (0.5 - 0.5 * theta / Math.PI);
        v = (float) (phi / Math.PI);
        return texture.getPixel(u, v);
    }

    private Vector3 getDirection(float u, float v) {
        Vector3 dest = new Vector3();
        double phi = 0, theta = 0;
        theta = u * 2 * Math.PI;
        phi = v * Math.PI;
        double sin_phi = Math.sin(phi);
        dest.x = (float) (-sin_phi * Math.cos(theta));
        dest.y = (float) Math.cos(phi);
        dest.z = (float) (sin_phi * Math.sin(theta));
        return dest;
    }

    public void scatterPhoton(ShadingState state, Color power) {
    }

    public float getPower() {
        return 0;
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -