Various Indigo lighting fixes (#640)

* Various lighting fixes

* 20w22a

* Various lighting fixes

Co-authored-by: modmuss50 <modmuss50@gmail.com>
This commit is contained in:
grondag 2020-05-29 12:06:29 -07:00 committed by GitHub
parent 1aa9d47288
commit 021baa0119
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 138 deletions

View file

@ -20,12 +20,6 @@ import java.nio.ByteOrder;
import it.unimi.dsi.fastutil.ints.Int2IntFunction;
import net.minecraft.client.render.model.BakedQuadFactory;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
/**
* Static routines of general utility for renderer implementations.
* Renderers are not required to use these helpers, but they were
@ -34,19 +28,6 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
public abstract class ColorHelper {
private ColorHelper() { }
/**
* Implement on quads to use methods that require it.
* Allows for much cleaner method signatures.
*/
public interface ShadeableQuad extends MutableQuadView {
boolean isFaceAligned();
boolean needsDiffuseShading(int textureIndex);
}
/** Same as vanilla values. */
private static final float[] FACE_SHADE_FACTORS = { 0.5F, 1.0F, 0.8F, 0.8F, 0.6F, 0.6F };
private static final Int2IntFunction colorSwapper = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? color -> ((color & 0xFF00FF00) | ((color & 0x00FF0000) >> 16) | ((color & 0xFF) << 16)) : color -> color;
/**
@ -64,109 +45,24 @@ public abstract class ColorHelper {
return color1;
}
int alpha = ((color1 >> 24) & 0xFF) * ((color2 >> 24) & 0xFF) / 0xFF;
int red = ((color1 >> 16) & 0xFF) * ((color2 >> 16) & 0xFF) / 0xFF;
int green = ((color1 >> 8) & 0xFF) * ((color2 >> 8) & 0xFF) / 0xFF;
int blue = (color1 & 0xFF) * (color2 & 0xFF) / 0xFF;
final int alpha = ((color1 >> 24) & 0xFF) * ((color2 >> 24) & 0xFF) / 0xFF;
final int red = ((color1 >> 16) & 0xFF) * ((color2 >> 16) & 0xFF) / 0xFF;
final int green = ((color1 >> 8) & 0xFF) * ((color2 >> 8) & 0xFF) / 0xFF;
final int blue = (color1 & 0xFF) * (color2 & 0xFF) / 0xFF;
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
/** Multiplies three lowest components by shade. High byte (usually alpha) unchanged. */
public static int multiplyRGB(int color, float shade) {
int alpha = ((color >> 24) & 0xFF);
int red = (int) (((color >> 16) & 0xFF) * shade);
int green = (int) (((color >> 8) & 0xFF) * shade);
int blue = (int) ((color & 0xFF) * shade);
final int alpha = ((color >> 24) & 0xFF);
final int red = (int) (((color >> 16) & 0xFF) * shade);
final int green = (int) (((color >> 8) & 0xFF) * shade);
final int blue = (int) ((color & 0xFF) * shade);
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
/**
* Same results as {@link BakedQuadFactory#method_3456(Direction)}.
*/
public static float diffuseShade(Direction direction) {
return FACE_SHADE_FACTORS[direction.getId()];
}
/**
* Formula mimics vanilla lighting for plane-aligned quads and
* is vaguely consistent with Phong lighting ambient + diffuse for others.
*/
public static float normalShade(float normalX, float normalY, float normalZ) {
return Math.min(0.5f + Math.abs(normalX) * 0.1f + (normalY > 0 ? 0.5f * normalY : 0) + Math.abs(normalZ) * 0.3f, 1f);
}
public static float normalShade(Vector3f normal) {
return normalShade(normal.getX(), normal.getY(), normal.getZ());
}
/**
* @see #diffuseShade
*/
public static float vertexShade(ShadeableQuad q, int vertexIndex, float faceShade) {
return q.hasNormal(vertexIndex) ? normalShade(q.normalX(vertexIndex), q.normalY(vertexIndex), q.normalZ(vertexIndex)) : faceShade;
}
/**
* Returns {@link #diffuseShade(Direction)} if quad is aligned to light face,
* otherwise uses face normal and {@link #normalShade}.
*/
public static float faceShade(ShadeableQuad quad) {
return quad.isFaceAligned() ? diffuseShade(quad.lightFace()) : normalShade(quad.faceNormal());
}
@FunctionalInterface
private interface VertexLighter {
void shade(ShadeableQuad quad, int vertexIndex, float shade);
}
private static VertexLighter[] VERTEX_LIGHTERS = new VertexLighter[8];
static {
VERTEX_LIGHTERS[0b000] = (q, i, s) -> { };
VERTEX_LIGHTERS[0b001] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s));
VERTEX_LIGHTERS[0b010] = (q, i, s) -> q.spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s));
VERTEX_LIGHTERS[0b011] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s)).spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s));
VERTEX_LIGHTERS[0b100] = (q, i, s) -> q.spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s));
VERTEX_LIGHTERS[0b101] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s)).spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s));
VERTEX_LIGHTERS[0b110] = (q, i, s) -> q.spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s)).spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s));
VERTEX_LIGHTERS[0b111] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s)).spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s)).spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s));
}
/**
* Honors vertex normals and uses non-cubic face normals for non-cubic quads.
*
* @param quad Quad to be shaded/unshaded.
*
* @param undo If true, will reverse prior application. Does not check that
* prior application actually happened. Use to "unbake" a quad.
* Some drift of colors may occur due to floating-point precision error.
*/
public static void applyDiffuseShading(ShadeableQuad quad, boolean undo) {
final float faceShade = faceShade(quad);
int i = quad.needsDiffuseShading(0) ? 1 : 0;
if (quad.needsDiffuseShading(1)) {
i |= 2;
}
if (quad.needsDiffuseShading(2)) {
i |= 4;
}
if (i == 0) {
return;
}
final VertexLighter shader = VERTEX_LIGHTERS[i];
for (int j = 0; j < 4; j++) {
final float vertexShade = vertexShade(quad, j, faceShade);
shader.shade(quad, j, undo ? 1f / vertexShade : vertexShade);
}
}
/**
* Component-wise max.
*/

View file

@ -19,7 +19,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
/**
@ -38,7 +37,7 @@ public class MeshBuilderImpl implements MeshBuilder {
protected void ensureCapacity(int stride) {
if (stride > limit - index) {
limit *= 2;
int[] bigger = new int[limit];
final int[] bigger = new int[limit];
System.arraycopy(data, 0, bigger, 0, index);
data = bigger;
maker.data = bigger;
@ -47,7 +46,7 @@ public class MeshBuilderImpl implements MeshBuilder {
@Override
public Mesh build() {
int[] packed = new int[index];
final int[] packed = new int[index];
System.arraycopy(data, 0, packed, 0, index);
index = 0;
maker.begin(data, index);
@ -76,7 +75,6 @@ public class MeshBuilderImpl implements MeshBuilder {
geometryFlags(GeometryHelper.computeShapeFlags(this));
}
ColorHelper.applyDiffuseShading(this, false);
index += EncodingFormat.TOTAL_STRIDE;
ensureCapacity(EncodingFormat.TOTAL_STRIDE);
baseIndex = index;

View file

@ -38,16 +38,14 @@ import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl.Value;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.NormalHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.TextureHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper.ShadeableQuad;
/**
* Almost-concrete implementation of a mutable quad. The only missing part is {@link #emit()},
* because that depends on where/how it is used. (Mesh encoding vs. render-time transformation).
*/
public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEmitter, ShadeableQuad {
public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEmitter {
public final void begin(int[] data, int baseIndex) {
this.data = data;
this.baseIndex = baseIndex;
@ -115,16 +113,6 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
return this;
}
@Override
public boolean isFaceAligned() {
return (geometryFlags() & GeometryHelper.AXIS_ALIGNED_FLAG) != 0;
}
@Override
public boolean needsDiffuseShading(int textureIndex) {
return textureIndex == 0 && !material().disableDiffuse(textureIndex);
}
@Override
public MutableQuadViewImpl pos(int vertexIndex, float x, float y, float z) {
final int index = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_X;

View file

@ -170,7 +170,7 @@ public class QuadViewImpl implements QuadView {
@Override
public void copyTo(MutableQuadView target) {
MutableQuadViewImpl quad = (MutableQuadViewImpl) target;
final MutableQuadViewImpl quad = (MutableQuadViewImpl) target;
// copy everything except the header/material
System.arraycopy(data, baseIndex + 1, quad.data, quad.baseIndex + 1, EncodingFormat.TOTAL_STRIDE - 1);
quad.isFaceNormalInvalid = this.isFaceNormalInvalid;
@ -288,7 +288,7 @@ public class QuadViewImpl implements QuadView {
}
public boolean hasShade() {
return shade;
return shade && !material().disableDiffuse(0);
}
public void shade(boolean shade) {

View file

@ -29,7 +29,6 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshImpl;
@ -58,7 +57,6 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
@Override
public Maker emit() {
lightFace(GeometryHelper.lightFace(this));
ColorHelper.applyDiffuseShading(this, false);
renderQuad(this);
clear();
return this;
@ -69,7 +67,7 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
@Override
public void accept(Mesh mesh) {
MeshImpl m = (MeshImpl) mesh;
final MeshImpl m = (MeshImpl) mesh;
final int[] data = m.data();
final int limit = data.length;
int index = 0;

View file

@ -25,14 +25,16 @@ import net.minecraft.block.BlockState;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix3f;
import net.minecraft.util.math.Matrix4f;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
/**
@ -139,7 +141,9 @@ public abstract class AbstractQuadRenderer {
/** for non-emissive mesh quads and all fallback quads with flat lighting. */
protected void tesselateFlat(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
colorizeQuad(quad, blockColorIndex);
final int brightness = ColorHelper.multiplyRGB(flatBrightness(quad, blockInfo.blockState, blockInfo.blockPos), blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade()));
shadeFlatQuad(quad);
final int brightness = flatBrightness(quad, blockInfo.blockState, blockInfo.blockPos);
for (int i = 0; i < 4; i++) {
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), brightness));
@ -151,6 +155,7 @@ public abstract class AbstractQuadRenderer {
/** for emissive mesh quads with flat lighting. */
protected void tesselateFlatEmissive(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
colorizeQuad(quad, blockColorIndex);
shadeFlatQuad(quad);
for (int i = 0; i < 4; i++) {
quad.lightmap(i, FULL_BRIGHTNESS);
@ -175,4 +180,68 @@ public abstract class AbstractQuadRenderer {
// Unfortunately cannot use brightness cache here unless we implement one specifically for flat lighting. See #329
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockState, mpos);
}
/**
* Starting in 1.16 flat shading uses dimension-specific diffuse factors that can be < 1.0
* even for un-shaded quads. These are also applied with AO shading but that is done in AO calculator.
*/
private void shadeFlatQuad(MutableQuadViewImpl quad) {
if ((quad.geometryFlags() & GeometryHelper.AXIS_ALIGNED_FLAG) == 0 || quad.hasVertexNormals()) {
// Quads that aren't direction-aligned or that have vertex normals need to be shaded
// using interpolation - vanilla can't handle them. Generally only applies to modded models.
final float faceShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
for (int i = 0; i < 4; i++) {
quad.spriteColor(i, 0, ColorHelper.multiplyRGB(quad.spriteColor(i, 0), vertexShade(quad, i, faceShade)));
}
} else {
final float diffuseShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
if (diffuseShade != 1.0f) {
for (int i = 0; i < 4; i++) {
quad.spriteColor(i, 0, ColorHelper.multiplyRGB(quad.spriteColor(i, 0), diffuseShade));
}
}
}
}
private float vertexShade(MutableQuadViewImpl quad, int vertexIndex, float faceShade) {
return quad.hasNormal(vertexIndex) ? normalShade(quad.normalX(vertexIndex), quad.normalY(vertexIndex), quad.normalZ(vertexIndex), quad.hasShade()) : faceShade;
}
/**
* Finds mean of per-face shading factors weighted by normal components.
* Not how light actually works but the vanilla diffuse shading model is a hack to start with
* and this gives reasonable results for non-cubic surfaces in a vanilla-style renderer.
*/
private float normalShade(float normalX, float normalY, float normalZ, boolean hasShade) {
float sum = 0;
float div = 0;
if (normalX > 0) {
sum += normalX * blockInfo.blockView.getBrightness(Direction.EAST, hasShade);
div += normalX;
} else if (normalX < 0) {
sum += -normalX * blockInfo.blockView.getBrightness(Direction.WEST, hasShade);
div -= normalX;
}
if (normalY > 0) {
sum += normalY * blockInfo.blockView.getBrightness(Direction.UP, hasShade);
div += normalY;
} else if (normalY < 0) {
sum += -normalY * blockInfo.blockView.getBrightness(Direction.DOWN, hasShade);
div -= normalY;
}
if (normalZ > 0) {
sum += normalZ * blockInfo.blockView.getBrightness(Direction.SOUTH, hasShade);
div += normalZ;
} else if (normalZ < 0) {
sum += -normalZ * blockInfo.blockView.getBrightness(Direction.NORTH, hasShade);
div -= normalZ;
}
return sum / div;
}
}

View file

@ -33,11 +33,11 @@ import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.render.model.json.ModelTransformation.Mode;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
@ -144,7 +144,6 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
@Override
public Maker emit() {
lightFace(GeometryHelper.lightFace(this));
ColorHelper.applyDiffuseShading(this, false);
renderQuad();
clear();
return this;