in-pass-shadows: add spot / point light support

in-pass-shadows
Kirill Vainer 7 years ago
parent 6fb2d029d2
commit caad16626e
  1. 23
      jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java
  2. 9
      jme3-core/src/main/java/com/jme3/material/logic/ShadowStaticPassLightingLogic.java
  3. 228
      jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java
  4. 2
      jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java
  5. 41
      jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java
  6. 14
      jme3-core/src/main/java/com/jme3/shadow/next/PreShadowArrayRenderer.java
  7. 183
      jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java
  8. 99
      jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java
  9. 1
      jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java
  10. 2
      jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java
  11. 10
      jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMapSlice.java
  12. 87
      jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java
  13. 92
      jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java
  14. 67
      jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java
  15. 23
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag
  16. 7
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert
  17. 121
      jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag
  18. 2
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert
  19. 15
      jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag
  20. 17
      jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md
  21. 151
      jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl
  22. 4
      jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib

@ -43,6 +43,7 @@ import com.jme3.scene.Mesh;
import com.jme3.scene.instancing.InstancedGeometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shadow.next.array.ArrayShadowMap;
import java.util.EnumSet;
public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
@ -85,6 +86,28 @@ public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
return filteredLightList;
}
protected float encodeLightType(Light light) {
switch (light.getType()) {
case Directional:
return 0.125f;
case Point:
return 0.25f;
case Spot:
return 0.5f;
default:
throw new UnsupportedOperationException("Invalid light type: " + light.getType());
}
}
protected float encodeLightTypeAndShadowMapIndex(Light light) {
if (light.getShadowMap() == null) {
return encodeLightType(light);
} else {
ArrayShadowMap map = (ArrayShadowMap) light.getShadowMap();
return -(encodeLightType(light) + map.getFirstArraySlice());
}
}
protected static ColorRGBA getAmbientColor(LightList lightList, boolean removeLights, ColorRGBA ambientLightColor) {
ambientLightColor.set(0, 0, 0, 1);
for (int j = 0; j < lightList.size(); j++) {

@ -138,15 +138,6 @@ public class ShadowStaticPassLightingLogic extends StaticPassLightingLogic {
defines.set(numPssmSplitsDefineId, pssmSplits);
}
@Override
protected float getShadowMapIndex(Light light) {
if (light.getShadowMap() == null) {
return -1.0f;
}
ArrayShadowMap map = (ArrayShadowMap) light.getShadowMap();
return (float) map.getFirstArraySlice();
}
@Override
protected void updateShadowUniforms(Renderer renderer, Shader shader, int nextTextureUnit) {
TextureArray array = null;

@ -34,13 +34,19 @@ package com.jme3.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.bounding.BoundingSphere;
import com.jme3.light.*;
import static com.jme3.light.Light.Type.Directional;
import static com.jme3.light.Light.Type.Spot;
import com.jme3.material.*;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.*;
import com.jme3.renderer.*;
import com.jme3.scene.Geometry;
import com.jme3.shader.*;
import com.jme3.util.TempVars;
import com.jme3.shadow.next.array.ArrayShadowMap;
import com.jme3.shadow.next.array.ArrayShadowMapSlice;
import com.jme3.shadow.next.array.DirectionalArrayShadowMap;
import com.jme3.texture.TextureArray;
import java.util.Comparator;
import java.util.EnumSet;
@ -49,10 +55,15 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING";
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS";
private static final String DEFINE_INDIRECT_LIGHTING = "INDIRECT_LIGHTING";
private static final String DEFINE_IN_PASS_SHADOWS = "IN_PASS_SHADOWS";
private static final String DEFINE_NUM_PSSM_SPLITS = "NUM_PSSM_SPLITS";
private static final RenderState ADDITIVE_LIGHT = new RenderState();
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
private LightProbe lightProbe = null;
private LightProbe lightProbe;
private TextureArray shadowMapArray;
private Vector3f pssmSplitsPositions;
private int numPssmSplits;
static {
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive);
@ -60,20 +71,24 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
}
private final int singlePassLightingDefineId;
private final int inPassShadowsDefineId;
private final int nbLightsDefineId;
private final int indirectLightingDefineId;
private final int numPssmSplitsDefineId;
public SinglePassAndImageBasedLightingLogic(TechniqueDef techniqueDef) {
super(techniqueDef);
numPssmSplitsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_PSSM_SPLITS, VarType.Int);
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean);
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int);
indirectLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_INDIRECT_LIGHTING, VarType.Boolean);
inPassShadowsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_IN_PASS_SHADOWS, VarType.Boolean);
}
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, Geometry geometry, DefineList defines) {
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
defines.set(singlePassLightingDefineId, true);
// TODO: here we have a problem, this is called once before render,
@ -82,15 +97,51 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
// first pass like ambient light in phong lighting.
// We cannot change the define between passes and the old technique, and
// for some reason the code fails on mac (renders nothing).
LightList lights = getFilteredLightList(renderManager, geometry);
if (lights != null) {
lightProbe = extractIndirectLights(lights, false);
if (lightProbe == null) {
defines.set(indirectLightingDefineId, false);
getFilteredLightList(renderManager, geometry);
ambientLightColor.set(0, 0, 0, 1);
lightProbe = null;
pssmSplitsPositions = null;
numPssmSplits = 0;
for (int i = 0; i < filteredLightList.size(); i++) {
Light light = filteredLightList.get(i);
if (light instanceof AmbientLight) {
ambientLightColor.addLocal(light.getColor());
filteredLightList.remove(i--);
} else if (light instanceof LightProbe) {
lightProbe = (LightProbe) light;
filteredLightList.remove(i--);
} else if (light.getShadowMap() != null) {
ArrayShadowMap shadowMap = (ArrayShadowMap) light.getShadowMap();
shadowMapArray = shadowMap.getArray();
if (light.getType() == Light.Type.Directional) {
numPssmSplits = shadowMap.getNumSlices();
pssmSplitsPositions = ((DirectionalArrayShadowMap) shadowMap).getProjectionSplitPositions();
}
}
}
ambientLightColor.a = 1.0f;
filteredLightList.sort(new Comparator<Light>() {
@Override
public int compare(Light a, Light b) {
boolean shadA = a.getShadowMap() != null;
boolean shadB = b.getShadowMap() != null;
if (shadA != shadB) {
return shadA ? -1 : 1;
} else {
defines.set(indirectLightingDefineId, true);
int ordA = a.getType().ordinal();
int ordB = b.getType().ordinal();
return ordB - ordA;
}
}
});
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
defines.set(indirectLightingDefineId, lightProbe != null);
defines.set(inPassShadowsDefineId, shadowMapArray != null);
defines.set(numPssmSplitsDefineId, numPssmSplits);
return super.makeCurrent(assetManager, renderManager, rendererCaps, geometry, defines);
}
@ -123,13 +174,11 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
Uniform shCoeffs = shader.getUniform("g_ShCoeffs");
Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap");
lightProbe = null;
if (startIndex != 0) {
// apply additive blending for 2nd and future passes
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT);
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
}else{
lightProbe = extractIndirectLights(lightList,true);
} else {
ambientColor.setValue(VarType.Vector4, ambientLightColor);
}
@ -147,81 +196,97 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
lightProbeData.setVector4InArray(0,0,0,-1, 0);
}
Uniform shadowMatricesUniform = shader.getUniform("g_ShadowMatrices");
shadowMatricesUniform.setMatrix4Length(numLights + numPssmSplits);
int shadowMatrixIndex = numPssmSplits;
int lightDataIndex = 0;
TempVars vars = TempVars.get();
Vector4f tmpVec = vars.vect4f1;
int curIndex;
int endIndex = numLights + startIndex;
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
int endIndex = Math.min(startIndex + numLights, lightList.size());
ArrayShadowMap directionalShadowMap = null;
for (curIndex = startIndex; curIndex < endIndex; curIndex++) {
Light light = lightList.get(curIndex);
Light l = lightList.get(curIndex);
if(l.getType() == Light.Type.Ambient){
endIndex++;
continue;
if (light.getType() == Light.Type.Ambient || light.getType() == Light.Type.Probe) {
throw new AssertionError();
}
ColorRGBA color = l.getColor();
//Color
if(l.getType() != Light.Type.Probe){
lightData.setVector4InArray(color.getRed(),
if (light.getShadowMap() != null) {
ArrayShadowMap shadowMap = (ArrayShadowMap) light.getShadowMap();
if (light.getType() == Directional) {
directionalShadowMap = shadowMap;
} else if (light.getType() == Spot) {
for (int j = 0; j < shadowMap.getNumSlices(); j++) {
ArrayShadowMapSlice slice = (ArrayShadowMapSlice) shadowMap.getSlice(j);
shadowMatricesUniform.setMatrix4InArray(
slice.getBiasedViewProjectionMatrix(),
shadowMatrixIndex);
shadowMatrixIndex++;
}
}
}
ColorRGBA color = light.getColor();
lightData.setVector4InArray(
color.getRed(),
color.getGreen(),
color.getBlue(),
l.getType().getId(),
lightDataIndex);
lightDataIndex++;
}
encodeLightTypeAndShadowMapIndex(light),
lightDataIndex++);
switch (l.getType()) {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
switch (light.getType()) {
case Directional: {
DirectionalLight dl = (DirectionalLight) light;
Vector3f dir = dl.getDirection();
//Data directly sent in view space to avoid a matrix mult for each pixel
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
lightDataIndex++;
lightData.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightDataIndex++);
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex++);
break;
case Point:
PointLight pl = (PointLight) l;
}
case Point: {
PointLight pl = (PointLight) light;
Vector3f pos = pl.getPosition();
float invRadius = pl.getInvRadius();
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
lightDataIndex++;
lightData.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightDataIndex++);
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex++);
break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
}
case Spot: {
SpotLight sl = (SpotLight) light;
Vector3f pos = sl.getPosition();
Vector3f dir = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
lightDataIndex++;
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
lightDataIndex++;
lightData.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRange, lightDataIndex++);
lightData.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), spotAngleCos, lightDataIndex++);
break;
}
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
throw new UnsupportedOperationException("Unknown type of light: " + light.getType());
}
}
// Padding of unsued buffer space
while (lightDataIndex < numLights * 3) {
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex++);
}
if (directionalShadowMap != null) {
for (int i = 0; i < numPssmSplits; i++) {
ArrayShadowMapSlice slice = (ArrayShadowMapSlice) directionalShadowMap.getSlice(i);
shadowMatricesUniform.setMatrix4InArray(slice.getBiasedViewProjectionMatrix(), i);
}
}
if (shadowMapArray != null) {
rm.getRenderer().setTexture(lastTexUnit, shadowMapArray);
shader.getUniform("g_ShadowMapArray").setValue(VarType.Int, lastTexUnit);
}
vars.release();
//Padding of unsued buffer space
while(lightDataIndex < numLights * 3) {
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
lightDataIndex++;
if (pssmSplitsPositions != null) {
shader.getUniform("g_PssmSplits").setValue(VarType.Vector3, pssmSplitsPositions);
}
return curIndex;
}
@ -230,41 +295,16 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
int nbRenderedLights = 0;
Renderer renderer = renderManager.getRenderer();
int batchSize = renderManager.getSinglePassLightBatchSize();
LightList lights = getFilteredLightList(renderManager, geometry);
if (lights.size() == 0) {
updateLightListUniforms(shader, geometry, lights,batchSize, renderManager, 0, lastTexUnit);
if (filteredLightList.size() == 0) {
updateLightListUniforms(shader, geometry, filteredLightList,batchSize, renderManager, 0, lastTexUnit);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
} else {
while (nbRenderedLights < lights.size()) {
nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights, lastTexUnit);
while (nbRenderedLights < filteredLightList.size()) {
nbRenderedLights = updateLightListUniforms(shader, geometry, filteredLightList, batchSize, renderManager, nbRenderedLights, lastTexUnit);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
}
}
protected LightProbe extractIndirectLights(LightList lightList, boolean removeLights) {
ambientLightColor.set(0, 0, 0, 1);
LightProbe probe = null;
for (int j = 0; j < lightList.size(); j++) {
Light l = lightList.get(j);
if (l instanceof AmbientLight) {
ambientLightColor.addLocal(l.getColor());
if(removeLights){
lightList.remove(l);
j--;
}
}
if (l instanceof LightProbe) {
probe = (LightProbe)l;
if(removeLights){
lightList.remove(l);
j--;
}
}
}
ambientLightColor.a = 1.0f;
return probe;
}
}

@ -138,7 +138,7 @@ public final class SinglePassLightingLogic extends DefaultTechniqueDefLogic {
lightData.setVector4InArray(color.getRed(),
color.getGreen(),
color.getBlue(),
l.getType().getId(),
encodeLightType(l),
lightDataIndex);
lightDataIndex++;

@ -126,10 +126,6 @@ public class StaticPassLightingLogic extends DefaultTechniqueDefLogic {
return techniqueDef.getShader(assetManager, rendererCaps, defines);
}
protected float getShadowMapIndex(Light light) {
return -1.0f;
}
protected void updateLightListUniforms(Matrix4f viewMatrix, Shader shader) {
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
ambientColor.setValue(VarType.Vector4, ambientLightColor);
@ -142,36 +138,41 @@ public class StaticPassLightingLogic extends DefaultTechniqueDefLogic {
lightData.setVector4Length(totalSize);
int index = 0;
for (SpotLight light : tempSpotLights) {
ColorRGBA color = light.getColor();
float lightTypeAndShadowMap = encodeLightTypeAndShadowMapIndex(light);
lightData.setVector4InArray(color.r, color.g, color.b, lightTypeAndShadowMap, index++);
tempPosition.set(light.getPosition());
float invRange = light.getInvSpotRange();
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRange, index++);
tempDirection.set(light.getDirection());
float spotAngleCos = light.getPackedAngleCos();
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, spotAngleCos, index++);
}
for (DirectionalLight light : tempDirLights) {
ColorRGBA color = light.getColor();
float shadowMapIndex = getShadowMapIndex(light);
float lightTypeAndShadowMap = encodeLightTypeAndShadowMapIndex(light);
lightData.setVector4InArray(color.r, color.g, color.b, lightTypeAndShadowMap, index++);
tempDirection.set(light.getDirection());
lightData.setVector4InArray(color.r, color.g, color.b, shadowMapIndex, index++);
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, 1f, index++);
}
for (PointLight light : tempPointLights) {
ColorRGBA color = light.getColor();
float shadowMapIndex = getShadowMapIndex(light);
float lightTypeAndShadowMap = encodeLightTypeAndShadowMapIndex(light);
lightData.setVector4InArray(color.r, color.g, color.b, lightTypeAndShadowMap, index++);
tempPosition.set(light.getPosition());
float invRadius = light.getInvRadius();
lightData.setVector4InArray(color.r, color.g, color.b, shadowMapIndex, index++);
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRadius, index++);
}
for (SpotLight light : tempSpotLights) {
ColorRGBA color = light.getColor();
float shadowMapIndex = getShadowMapIndex(light);
tempPosition.set(light.getPosition());
tempDirection.set(light.getDirection());
float invRange = light.getInvSpotRange();
float spotAngleCos = light.getPackedAngleCos();
lightData.setVector4InArray(color.r, color.g, color.b, shadowMapIndex, index++);
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRange, index++);
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, spotAngleCos, index++);
}
}
protected void updateShadowUniforms(Renderer renderer, Shader shader, int nextTextureUnit) {

@ -31,6 +31,7 @@
*/
package com.jme3.shadow.next;
import com.jme3.asset.AssetManager;
import com.jme3.shadow.next.pssm.DirectionalShadowParameters;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
@ -48,6 +49,7 @@ import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.renderer.queue.OpaqueComparator;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.shader.VarType;
import com.jme3.shadow.next.array.DirectionalArrayShadowMap;
import com.jme3.shadow.next.array.PointArrayShadowMap;
@ -105,13 +107,15 @@ public class PreShadowArrayRenderer implements SceneProcessor {
array.setAnisotropicFilter(1);
array.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
array.setMagFilter(MagFilter.Nearest);
array.setMinFilter(MinFilter.NearestNoMipMaps);
array.setMagFilter(MagFilter.Bilinear);
array.setMinFilter(MinFilter.BilinearNoMipMaps);
}
public void displayDebug(AssetManager assetManager, Node guiRoot) {
guiRoot.addControl(new ShadowDebugControl(assetManager, this));
}
@Override
public void initialize(RenderManager rm, ViewPort vp) {
this.renderManager = rm;
@ -135,10 +139,14 @@ public class PreShadowArrayRenderer implements SceneProcessor {
this.textureSize = textureSize;
}
public TextureArray getShadowMapTexture() {
return array;
}
public void addLight(Light light) {
if (array.getImage() == null) {
array.setImage(new Image(
Format.Depth16,
Format.Depth32F,
textureSize,
textureSize,
0,

@ -0,0 +1,183 @@
/*
* Copyright (c) 2009-2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.shadow.next;
import com.jme3.shadow.next.pssm.DirectionalShadowParameters;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.material.RenderState;
import com.jme3.math.Vector3f;
import com.jme3.post.SceneProcessor;
import com.jme3.profile.AppProfiler;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.renderer.queue.OpaqueComparator;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.shadow.next.pssm.DirectionalShadowMap;
import com.jme3.texture.FrameBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* The 4th generation of shadow mapping in jME3.
* <p>
* This version is primarily focused on rendering in-pass shadows, so pre-pass
* and subsequent stages are separated.
*
* @author Kirill Vainer
*/
public class PreShadowRenderer implements SceneProcessor {
private static final String PRE_SHADOW_TECHNIQUE_NAME = "PreShadow";
private RenderManager renderManager;
private ViewPort viewPort;
private final Vector3f[] points = new Vector3f[8];
private final GeometryList shadowCasters = new GeometryList(new OpaqueComparator());
private final List<ShadowMap> shadowMaps = new ArrayList<>();
private final RenderState prePassRenderState = RenderState.ADDITIONAL.clone();
private int textureSize = 1024;
// parameters for directional lights
private final DirectionalShadowParameters directionalParams = new DirectionalShadowParameters();
public PreShadowRenderer() {
for (int i = 0; i < points.length; i++) {
points[i] = new Vector3f();
}
prePassRenderState.setFaceCullMode(RenderState.FaceCullMode.Off);
prePassRenderState.setColorWrite(false);
prePassRenderState.setDepthWrite(true);
prePassRenderState.setDepthTest(true);
prePassRenderState.setPolyOffset(1.2f, 0);
}
@Override
public void initialize(RenderManager rm, ViewPort vp) {
this.renderManager = rm;
this.viewPort = vp;
}
public DirectionalShadowParameters directional() {
return directionalParams;
}
public void setPolyOffset(float factor, float units) {
// TODO: might want to set this separately per model
prePassRenderState.setPolyOffset(factor, units);
}
public int getTextureSize() {
return textureSize;
}
public void setTextureSize(int textureSize) {
// TODO: support changing texture size after shadow maps are created
this.textureSize = textureSize;
}
public void addLight(Light light) {
ShadowMap shadowMap;
switch (light.getType()) {
case Directional:
shadowMap = new DirectionalShadowMap(
(DirectionalLight) light,
textureSize,
directionalParams.getNumSplits(),
points);
break;
default:
throw new UnsupportedOperationException();
}
light.setShadowMap(shadowMap);
shadowMaps.add(shadowMap);
}
@Override
public void reshape(ViewPort vp, int w, int h) {
}
@Override
public boolean isInitialized() {
return this.viewPort != null;
}
@Override
public void preFrame(float tpf) {
}
private void renderShadowMaps() {
renderManager.setForcedRenderState(prePassRenderState);
renderManager.setForcedTechnique(PRE_SHADOW_TECHNIQUE_NAME);
for (ShadowMap shadowMap : shadowMaps) {
switch (shadowMap.getLightType()) {
case Directional:
DirectionalShadowMap directionalShadow = (DirectionalShadowMap) shadowMap;
directionalShadow.renderShadowMap(renderManager, viewPort, directionalParams, shadowCasters);
break;
default:
throw new UnsupportedOperationException();
}
}
Renderer renderer = renderManager.getRenderer();
renderer.setFrameBuffer(viewPort.getOutputFrameBuffer());
renderManager.setForcedRenderState(null);
renderManager.setForcedTechnique(null);
renderManager.setCamera(viewPort.getCamera(), false);
}
@Override
public void postQueue(RenderQueue rq) {
directionalParams.updateSplitPositions(viewPort.getCamera());
renderShadowMaps();
}
@Override
public void postFrame(FrameBuffer out) {
}
@Override
public void cleanup() {
}
@Override
public void setProfiler(AppProfiler profiler) {
}
}

@ -0,0 +1,99 @@
/*
* Copyright (c) 2009-2017 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.shadow.next;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.shader.VarType;
import com.jme3.texture.Image;
import com.jme3.texture.TextureArray;
import com.jme3.ui.Picture;
import java.util.ArrayList;
import java.util.List;
/**
* Shows the shadow maps on the screen
*
* @author Kirill Vainer
*/
final class ShadowDebugControl extends AbstractControl {
private final List<Picture> pictures = new ArrayList<>();
public ShadowDebugControl(AssetManager assetManager, PreShadowArrayRenderer shadowRenderer) {
TextureArray shadowMapArray = shadowRenderer.getShadowMapTexture();
Image shadowMap = shadowMapArray.getImage();
for (int i = 0; i < shadowMap.getDepth(); i++) {
Picture picture = new Picture("Shadow Map " + i);
picture.setPosition(20, i * 128 + 20);
picture.setWidth(128);
picture.setHeight(128);
Material material = new Material(assetManager, "Common/MatDefs/Shadow/ShowShadowArray.j3md");
material.setTexture("ShadowMapArray", shadowMapArray);
material.setFloat("ShadowMapSlice", i);
picture.setMaterial(material);
pictures.add(picture);
}
}
@Override
public void setSpatial(Spatial spatial) {
if (spatial != null) {
for (Picture picture : pictures) {
((Node) spatial).detachChild(picture);
}
}
super.setSpatial(spatial);
if (spatial != null) {
for (Picture picture : pictures) {
((Node) spatial).attachChild(picture);
}
}
}
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
}

@ -82,6 +82,7 @@ public class BaseArrayShadowMapSlice<T extends Light> implements ArrayShadowMapS
if (fbNeedClear) {
renderer.setFrameBuffer(frameBuffer);
renderer.clearClipRect();
renderer.clearBuffers(false, true, false);
fbNeedClear = false;
}

@ -51,7 +51,7 @@ public class PointArrayShadowMapSlice extends BaseArrayShadowMapSlice<PointLight
}
public void updateShadowCamera(ViewPort viewPort, PointLight light, GeometryList shadowCasters) {
shadowCamera.setFrustumPerspective(90f, 1f, 0.1f, light.getRadius());
shadowCamera.setFrustumPerspective(90f, 1f, 0.5f, light.getRadius());
shadowCamera.setLocation(light.getPosition());
for (Spatial scene : viewPort.getScenes()) {
ShadowUtil.getGeometriesInCamFrustum(scene, shadowCamera, ShadowMode.Cast, shadowCasters);

@ -50,9 +50,19 @@ public class SpotArrayShadowMapSlice extends BaseArrayShadowMapSlice<SpotLight>
super(array, layer, textureSize, true);
}
private static boolean isParallelToYUp(Vector3f direction) {
return direction.x == 0 && direction.z == 0
&& (direction.y == -1 || direction.y == 1);
}
public void updateShadowCamera(ViewPort viewPort, SpotLight light, GeometryList shadowCasters) {
shadowCamera.setLocation(light.getPosition());
if (isParallelToYUp(light.getDirection())) {
// direction and up cannot be parallel
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Z);
} else {
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Y);
}
shadowCamera.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1, light.getSpotRange());
for (Spatial scene : viewPort.getScenes()) {
ShadowUtil.getGeometriesInCamFrustum(scene, shadowCamera, RenderQueue.ShadowMode.Cast, shadowCasters);

@ -0,0 +1,87 @@
/*
* Copyright (c) 2009-2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.shadow.next.pssm;
import com.jme3.light.Light;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
import com.jme3.texture.Texture.MagFilter;
import com.jme3.texture.Texture.MinFilter;
import com.jme3.texture.Texture.ShadowCompareMode;
import com.jme3.texture.Texture2D;
import com.jme3.shadow.next.ShadowMapSlice;
public abstract class BaseShadowMapSlice<T extends Light> implements ShadowMapSlice<T> {
protected final FrameBuffer frameBuffer;
protected final Texture2D depthTexture;
protected final Camera shadowCamera;
protected final Vector3f[] points;
public BaseShadowMapSlice(int size, Vector3f[] points) {
this.depthTexture = new Texture2D(size, size, Image.Format.Depth16);
this.depthTexture.setAnisotropicFilter(1);
this.depthTexture.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
this.depthTexture.setMagFilter(MagFilter.Bilinear);
this.depthTexture.setMinFilter(MinFilter.BilinearNoMipMaps);
this.shadowCamera = new Camera(size, size);
this.frameBuffer = new FrameBuffer(size, size, 1);
this.frameBuffer.setDepthTexture(depthTexture);
this.points = points;
}
@Override
public void renderShadowMap(RenderManager renderManager, T light, ViewPort viewPort, GeometryList shadowCasters) {
Renderer renderer = renderManager.getRenderer();
renderer.setFrameBuffer(frameBuffer);
renderer.clearBuffers(false, true, false);
if (shadowCasters.size() > 0) {
renderManager.setCamera(shadowCamera, false);
viewPort.getQueue().renderShadowQueue(shadowCasters, renderManager, shadowCamera, true);
}
}
@Override
public Matrix4f getBiasedViewProjectionMatrix() {
// return shadowCamera.getViewProjectionMatrix();
throw new UnsupportedOperationException();
}
}

@ -0,0 +1,92 @@
/*
* Copyright (c) 2009-2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.shadow.next.pssm;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light.Type;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.shadow.next.ShadowMapSlice;
import com.jme3.shadow.next.ShadowMap;
/**
* @author Kirill Vainer
*/
public class DirectionalShadowMap implements ShadowMap<DirectionalLight> {
private final DirectionalLight light;
private final DirectionalShadowMapSlice[] splits;
private final Vector3f projectionSplitPositions = new Vector3f();
public DirectionalShadowMap(DirectionalLight light, int textureSize, int numSplits, Vector3f[] points) {
this.light = light;
this.splits = new DirectionalShadowMapSlice[numSplits];
for (int i = 0; i < splits.length; i++) {
this.splits[i] = new DirectionalShadowMapSlice(textureSize, points);
}
}
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, DirectionalShadowParameters params, GeometryList shadowCasters) {
projectionSplitPositions.set(params.getProjectionSplitPositions());
float[] splitPositionsViewSpace = params.getSplitPositions();
for (int i = 0; i < splits.length; i++) {
float near = splitPositionsViewSpace[i];
float far = splitPositionsViewSpace[i + 1];
shadowCasters.clear();
splits[i].updateShadowCamera(viewPort, light, shadowCasters, near, far);
splits[i].renderShadowMap(renderManager, light, viewPort, shadowCasters);
}
}
public Vector3f getProjectionSplitPositions() {
return projectionSplitPositions;
}
@Override
public int getNumSlices() {
return splits.length;
}
@Override
public ShadowMapSlice getSlice(int index) {
return splits[index];
}
@Override
public Type getLightType() {
return Type.Directional;
}
}

@ -0,0 +1,67 @@
/*
* Copyright (c) 2009-2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.shadow.next.pssm;
import com.jme3.light.DirectionalLight;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.shadow.ShadowUtil;
import com.jme3.texture.Texture2D;
/**
* @author Kirill Vainer
*/
public class DirectionalShadowMapSlice extends BaseShadowMapSlice<DirectionalLight> {
public DirectionalShadowMapSlice(int size, Vector3f[] points) {
super(size, points);
this.shadowCamera.setParallelProjection(true);
}
public void updateShadowCamera(
ViewPort viewPort,
DirectionalLight light,
GeometryList shadowCasters,
float near,
float far) {
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), near, far, points);
shadowCamera.lookAtDirection(light.getDirection(), shadowCamera.getUp());
int textureSize = frameBuffer.getWidth();
ShadowUtil.updateShadowCamera(viewPort, null, shadowCamera, points, shadowCasters, textureSize);
}
public Texture2D getTexture() {
return depthTexture;
}
}

@ -2,7 +2,7 @@
#import "Common/ShaderLib/PBR.glsllib"
#import "Common/ShaderLib/Parallax.glsllib"
#import "Common/ShaderLib/Lighting.glsllib"
#import "Common/ShaderLib/InPassShadows.glsl"
varying vec2 texCoord;
#ifdef SEPARATE_TEXCOORD
@ -28,6 +28,8 @@ varying vec3 wPosition;
uniform vec4 g_LightProbeData;
#endif
uniform vec4 g_AmbientLightColor;
#ifdef BASECOLORMAP
uniform sampler2D m_BaseColorMap;
#endif
@ -87,7 +89,7 @@ varying vec3 wNormal;
uniform float m_AlphaDiscardThreshold;
#endif
void main(){
void main() {
vec2 newTexCoord;
vec3 viewDir = normalize(g_CameraPosition - wPosition);
@ -216,10 +218,19 @@ void main(){
specularColor.rgb *= lightMapColor;
#endif
Shadow_ProcessPssmSlice();
float ndotv = max( dot( normal, viewDir ),0.0);
for( int i = 0;i < NB_LIGHTS; i+=3){
vec4 lightColor = g_LightData[i];
float shadowMapIndex = -1.0;
if (lightColor.w < 0.0) {
shadowMapIndex = floor(-lightColor.w);
lightColor.w = fract(-lightColor.w);
}
vec4 lightData1 = g_LightData[i+1];
vec4 lightDir;
vec3 lightVec;
@ -228,7 +239,7 @@ void main(){
float fallOff = 1.0;
#if __VERSION__ >= 110
// allow use of control flow
if(lightColor.w > 1.0){
if(lightColor.w > 0.4){
#endif
fallOff = computeSpotFalloff(g_LightData[i+2], lightDir.xyz);
#if __VERSION__ >= 110
@ -237,6 +248,10 @@ void main(){
//point light attenuation
fallOff *= lightDir.w;
if (shadowMapIndex >= 0.0) {
fallOff *= Shadow_Process(i / 3, lightColor.w, shadowMapIndex, lightVec, lightDir.xyz, wPosition, lightData1.w);
}
vec3 directDiffuse;
vec3 directSpecular;
@ -249,6 +264,8 @@ void main(){
gl_FragColor.rgb += directLighting * fallOff;
}
gl_FragColor.rgb += g_AmbientLightColor.rgb * diffuseColor.rgb;
#ifdef INDIRECT_LIGHTING
vec3 rv = reflect(-viewDir.xyz, normal.xyz);
//prallax fix for spherical bounds from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/

@ -1,6 +1,7 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#import "Common/ShaderLib/Instancing.glsllib"
#import "Common/ShaderLib/Skinning.glsllib"
#import "Common/ShaderLib/InPassShadows.glsl"
uniform vec4 m_BaseColor;
@ -52,7 +53,11 @@ void main(){
texCoord2 = inTexCoord2;
#endif
wPosition = TransformWorld(modelSpacePos).xyz;
vec3 worldPos = TransformWorld(modelSpacePos).xyz;
Shadow_ProcessProjCoord(worldPos);
wPosition = worldPos;
wNormal = TransformWorldNormal(modelSpaceNorm);
#if defined(NORMALMAP) || defined(PARALLAXMAP)

@ -1,5 +1,4 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#import "Common/ShaderLib/BlinnPhongLighting.glsllib"
#import "Common/ShaderLib/Lighting.glsllib"
#import "Common/ShaderLib/InPassShadows.glsl"
#import "Common/ShaderLib/PBR.glsllib"
@ -34,7 +33,7 @@
#define SPOT_LIGHT_START (SPOT_SHADOW_LIGHT_END)
#define SPOT_LIGHT_END (SPOT_LIGHT_START + NUM_SPOT_LIGHTS * 3)
#define LIGHT_DATA_SIZE (SPOT_LIGHT_END)
#define LIGHT_DATA_SIZE (NUM_SHADOW_DIR_LIGHTS * 2 )
uniform vec3 g_CameraPosition;
@ -64,72 +63,34 @@ struct surface_t {
float ndotv;
};
float Lighting_ProcessAttenuation(float invRadius, float dist) {
#ifdef SRGB
float invRadTimesDist = invRadius * dist;
float atten = (1.0 - invRadTimesDist) / (1.0 + invRadTimesDist * dist);
return clamp(atten, 0.0, 1.0);
#else
return max(0.0, 1.0 - invRadius * dist);
#endif
}
void Lighting_ProcessDirectional(int lightIndex, surface_t surface, out vec3 outDiffuse, out vec3 outSpecular) {
void Lighting_Process(in int lightIndex, in surface_t surface, out vec3 outDiffuse, out vec3 outSpecular, inout int startProjIndex) {
vec4 lightColor = g_LightData[lightIndex];
vec3 lightDirection = g_LightData[lightIndex + 1].xyz;
PBR_ComputeDirectLightSpecWF(surface.normal, -lightDirection, surface.viewDir,
lightColor.rgb, surface.specular.rgb, surface.roughness, surface.ndotv,
outDiffuse, outSpecular);
}
vec4 lightData1 = g_LightData[lightIndex + 1];
float shadowMapIndex = -1.0;
vec3 Lighting_ProcessPoint(in int lightIndex, in surface_t surface, out vec3 outDiffuse, out vec3 outSpecular) {
vec4 lightColor = g_LightData[lightIndex];
vec4 lightPosition = g_LightData[lightIndex + 1];
vec3 lightDirection = lightPosition.xyz - surface.position;
float dist = length(lightDirection);
lightDirection /= vec3(dist);
float atten = Lighting_ProcessAttenuation(lightPosition.w, dist);
if (atten == 0.0) {
outDiffuse = vec3(0.0);
outSpecular = vec3(0.0);
return lightDirection;
if (lightColor.w < 0.0) {
lightColor.w = -lightColor.w;
shadowMapIndex = floor(lightColor.w);
lightColor.w = lightColor.w - shadowMapIndex;
}
PBR_ComputeDirectLightSpecWF(surface.normal, lightDirection, surface.viewDir,
lightColor.rgb, surface.specular.rgb, surface.roughness, surface.ndotv,
outDiffuse, outSpecular);
outDiffuse *= atten;
outSpecular *= atten;
vec4 lightDir;
vec3 lightVec;
lightComputeDir(surface.position, lightColor.w, lightData1, lightDir, lightVec);
return lightDirection;
}
void Lighting_ProcessSpot(in int lightIndex, in surface_t surface, out vec3 outDiffuse, out vec3 outSpecular) {
vec4 lightColor = g_LightData[lightIndex];
vec4 lightPosition = g_LightData[lightIndex + 1];
vec4 lightDirection = g_LightData[lightIndex + 2];
vec3 lightVector = lightPosition.xyz - surface.position;
float dist = length(lightVector);
lightVector /= vec3(dist);
float atten = computeSpotFalloff(lightDirection, lightVector);
if (atten == 0.0) {
outDiffuse = vec3(0.0);
outSpecular = vec3(0.0);
return;
if (shadowMapIndex >= 0.0) {
lightDir.w *= Shadow_Process(lightColor.w, lightDir.xyz, shadowMapIndex, startProjIndex);
}
atten *= Lighting_ProcessAttenuation(lightPosition.w, dist);
if (atten == 0.0) {
outDiffuse = vec3(0.0);
outSpecular = vec3(0.0);
return;
if (lightColor.w >= 0.5) {
lightDir.w *= computeSpotFalloff(g_LightData[lightIndex + 2], lightDir.xyz);
}
PBR_ComputeDirectLightSpecWF(surface.normal, lightVector, surface.viewDir,
lightColor.rgb *= lightDir.w;
PBR_ComputeDirectLightSpecWF(surface.normal, lightDir.xyz, surface.viewDir,
lightColor.rgb, surface.specular.rgb, surface.roughness, surface.ndotv,
outDiffuse, outSpecular);
outDiffuse *= atten;
outSpecular *= atten;
}
void Lighting_ProcessAll(surface_t surface, out vec3 ambient, out vec3 diffuse, out vec3 specular) {
@ -143,62 +104,50 @@ void Lighting_ProcessAll(surface_t surface, out vec3 ambient, out vec3 diffuse,
#if LIGHT_DATA_SIZE > 0
int projIndex = 0;
for (int i = DIR_SHADOW_LIGHT_START; i < DIR_SHADOW_LIGHT_END; i += 2) {
for (int i = SPOT_SHADOW_LIGHT_START; i < SPOT_SHADOW_LIGHT_END; i += 3) {
vec3 outDiffuse, outSpecular;
Lighting_ProcessDirectional(i, surface, outDiffuse, outSpecular);
float shadow = Shadow_Process(0, vec3(0.0), g_LightData[i].w, projIndex);
outDiffuse *= shadow;
outSpecular *= shadow;
Lighting_Process(i, surface, outDiffuse, outSpecular, projIndex);
diffuse += outDiffuse;
specular += outSpecular;
}
for (int i = DIR_LIGHT_START; i < DIR_LIGHT_END; i += 2) {
for (int i = SPOT_LIGHT_START; i < SPOT_LIGHT_END; i += 3) {
vec3 outDiffuse, outSpecular;
Lighting_ProcessDirectional(i, surface, outDiffuse, outSpecular);
Lighting_Process(i, surface, outDiffuse, outSpecular, projIndex);
diffuse += outDiffuse;
specular += outSpecular;
}
for (int i = POINT_SHADOW_LIGHT_START; i < POINT_SHADOW_LIGHT_END; i += 2) {
for (int i = DIR_SHADOW_LIGHT_START; i < DIR_SHADOW_LIGHT_END; i += 2) {
vec3 outDiffuse, outSpecular;
vec3 lightDir = Lighting_ProcessPoint(i, surface, outDiffuse, outSpecular);
float shadow = Shadow_Process(1, lightDir, g_LightData[i].w, projIndex);
outDiffuse *= shadow;
outSpecular *= shadow;
Lighting_Process(i, surface, outDiffuse, outSpecular, projIndex);
diffuse += outDiffuse;
specular += outSpecular;
}
for (int i = POINT_LIGHT_START; i < POINT_LIGHT_END; i += 2) {
for (int i = DIR_LIGHT_START; i < DIR_LIGHT_END; i += 2) {
vec3 outDiffuse, outSpecular;
Lighting_ProcessPoint(i, surface, outDiffuse, outSpecular);
Lighting_Process(i, surface, outDiffuse, outSpecular, projIndex);
diffuse += outDiffuse;
specular += outSpecular;
}
for (int i = SPOT_SHADOW_LIGHT_START; i < SPOT_SHADOW_LIGHT_END; i += 3) {
for (int i = POINT_SHADOW_LIGHT_START; i < POINT_SHADOW_LIGHT_END; i += 2) {
vec3 outDiffuse, outSpecular;
Lighting_ProcessSpot(i, surface, outDiffuse, outSpecular);
float shadow = Shadow_Process(2, vec3(0.0), g_LightData[i].w, projIndex);
outDiffuse *= shadow;
outSpecular *= shadow;
Lighting_Process(i, surface, outDiffuse, outSpecular, projIndex);
diffuse += outDiffuse;
specular += outSpecular;
}
for (int i = SPOT_LIGHT_START; i < SPOT_LIGHT_END; i += 3) {
for (int i = POINT_LIGHT_START; i < POINT_LIGHT_END; i += 2) {
vec3 outDiffuse, outSpecular;
Lighting_ProcessSpot(i, surface, outDiffuse, outSpecular);
Lighting_Process(i, surface, outDiffuse, outSpecular, projIndex);
diffuse += outDiffuse;
specular += outSpecular;
}
#endif
}

@ -29,7 +29,7 @@ void main() {
vec3 lightDir = g_CameraPosition - TransformWorld(modelSpacePos).xyz;
// The Z value to write into the depth map, should be [0.0, 1.0]
float z = length(lightDir) / g_FrustumNearFar.y;
float z = sqrt(length(lightDir) / g_FrustumNearFar.y);
// Remap [0.0, 1.0] into [-1.0, 1.0]
gl_Position.z = (clamp(z, 0.0, 1.0) * 2.0 - 1.0) * gl_Position.w;

@ -0,0 +1,15 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform float m_ShadowMapSlice;
uniform sampler2DArray m_ShadowMapArray;
varying vec2 texCoord1;
void main() {
float shadow = texture2D(m_ShadowMapArray, vec3(texCoord1, m_ShadowMapSlice)).r;
shadow = sqrt(shadow);
// TODO: make it betterer
gl_FragColor.rgb = vec3(shadow);
gl_FragColor.a = 1.0;
}

@ -0,0 +1,17 @@
MaterialDef Pre Shadow {
MaterialParameters {
TextureArray ShadowMapArray
Float ShadowMapSlice
}
Technique {
VertexShader GLSL150 : Common/MatDefs/Misc/Unshaded.vert
FragmentShader GLSL150 : Common/MatDefs/Shadow/ShowShadowArray.frag
WorldParameters {
WorldViewProjectionMatrix
ViewProjectionMatrix
}
}
}

@ -1,100 +1,98 @@
#ifndef NUM_SHADOW_DIR_LIGHTS
#define NUM_SHADOW_DIR_LIGHTS 0
#endif
#ifndef NUM_SHADOW_POINT_LIGHTS
#define NUM_SHADOW_POINT_LIGHTS 0
#endif
#ifndef NUM_SHADOW_SPOT_LIGHTS
#define NUM_SHADOW_SPOT_LIGHTS 0
#endif
#import "Common/ShaderLib/GLSLCompat.glsllib"
#extension GL_EXT_texture_array : enable
#ifndef NUM_PSSM_SPLITS
#define NUM_PSSM_SPLITS 0
#endif
#define SHADOW_DATA_SIZE (NUM_SHADOW_DIR_LIGHTS * NUM_PSSM_SPLITS + NUM_SHADOW_POINT_LIGHTS * 6 + NUM_SHADOW_SPOT_LIGHTS)
#ifdef IN_PASS_SHADOWS
#if SHADOW_DATA_SIZE > 0
uniform mat4 g_ShadowMatrices[(NB_LIGHTS/3) + NUM_PSSM_SPLITS];
varying vec4 vProjCoord[SHADOW_DATA_SIZE];
#if NUM_PSSM_SPLITS > 0
varying vec3 dirProjCoord[NUM_PSSM_SPLITS];
#else
varying vec3 dirProjCoord[1];
#endif
#ifdef VERTEX_SHADER
uniform mat4 g_ShadowMatrices[SHADOW_DATA_SIZE];
void Shadow_ProcessProjCoord(vec3 worldPos) {
for (int i = 0; i < SHADOW_DATA_SIZE; i++) {
vProjCoord[i] = g_ShadowMatrices[i] * vec4(worldPos, 1.0);
#if NUM_PSSM_SPLITS > 0
for (int i = 0; i < NUM_PSSM_SPLITS; i++) {
#if __VERSION__ >= 150
dirProjCoord[i] = mat4x3(g_ShadowMatrices[i]) * vec4(worldPos, 1.0);
#else
dirProjCoord[i] = (g_ShadowMatrices[i] * vec4(worldPos, 1.0)).xyz;
#endif
}
#endif
}
#else
uniform sampler2DArrayShadow g_ShadowMapArray;
uniform vec3 g_PssmSplits;
int pssmSliceOffset;
float pssmSliceOffset;
void Shadow_ProcessPssmSlice() {
#if defined(NUM_PSSM_SPLITS) && NUM_PSSM_SPLITS > 1
pssmSliceOffset = int(dot(step(g_PssmSplits.xyz, gl_FragCoord.zzz), vec3(1.0)));
#if NUM_PSSM_SPLITS > 1
pssmSliceOffset = dot(step(g_PssmSplits.xyz, gl_FragCoord.zzz), vec3(1.0));
#else
pssmSliceOffset = 0;
pssmSliceOffset = 0.0;
#endif
}
/**
* Returns a float from 0.0 - 5.0 containing the index
* of the cubemap face to fetch for the given direction
*/
float Shadow_GetCubeMapFace(in vec3 direction) {
vec3 mag = abs(direction);
// Compare each component against the other two
// Largest component is set to 1.0, the rest are 0.0
vec3 largestComp = step(mag.yzx, mag) * step(mag.zxy, mag);
// Negative components are set to 1.0, the positive are 0.0
vec3 negComp = step(direction, vec3(0.0));
// Each component contains the face index to use
vec3 faceIndices = vec3(0.0, 2.0, 4.0) + negComp;
// Pick the face index with the largest component
return dot(largestComp, faceIndices);
vec3 Shadow_GetCubeMapTC(in vec3 direction) {
vec3 axis = abs(direction);
float largest = max(axis.x, max(axis.y, axis.z));
vec3 tc;
if (largest == axis.x) {
if (direction.x > 0.0) {
tc = vec3( direction.z, -direction.y, 0.0);
} else {
tc = vec3(-direction.z, -direction.y, 1.0);
}
float Shadow_ProcessDirectional(in int lightType, in vec3 lightDir, in float startArrayLayer, inout int startProjIndex) {
float arraySlice = startArrayLayer + float(pssmSliceOffset);
vec3 projCoord = vProjCoord[startProjIndex + pssmSliceOffset].xyz;
startProjIndex += NUM_PSSM_SPLITS;
return texture(g_ShadowMapArray, vec4(projCoord.xy, arraySlice, projCoord.z));
} else if (largest == axis.y) {
if (direction.y > 0.0) {
tc = vec3(-direction.x, direction.z, 2.0);
} else {
tc = vec3(-direction.x, -direction.z, 3.0);
}
} else {
if (direction.z > 0.0) {
tc = vec3(-direction.x, -direction.y, 4.0);
} else {
tc = vec3(direction.x, -direction.y, 5.0);
}
float Shadow_ProcessSpot(in int lightType, in vec3 lightDir, in float startArrayLayer, inout int startProjIndex) {
vec4 projCoord = vProjCoord[startProjIndex];
projCoord.xyz /= projCoord.w;
startProjIndex ++;
return texture(g_ShadowMapArray, vec4(projCoord.xy, startArrayLayer, projCoord.z));
}
largest = 1.0 / largest;
tc.xy = 0.5 * (tc.xy * vec2(largest) + 1.0);
return tc;
}
float Shadow_Process(in int lightType, in vec3 lightDir, in float startArrayLayer, inout int startProjIndex) {
float arraySlice = startArrayLayer;
vec4 projCoord;
float Shadow_Process(int lightIndex, float lightType, float shadowMapIndex,
vec3 lightVec, vec3 lightDir,
vec3 worldPos, float invRadius) {
vec4 tc;
if (lightType == 0) {
arraySlice += float(pssmSliceOffset);
projCoord = vProjCoord[startProjIndex + pssmSliceOffset];
startProjIndex += NUM_PSSM_SPLITS;
} else if (lightType == 1) {
float face = Shadow_GetCubeMapFace(lightDir);
arraySlice += face;
projCoord = vProjCoord[startProjIndex + int(face)];
projCoord.xyz /= projCoord.w;
startProjIndex += 6;
if (lightType <= 0.2) {
vec3 projCoord = dirProjCoord[int(pssmSliceOffset)];
tc = vec4(projCoord.xy, shadowMapIndex + pssmSliceOffset, projCoord.z);
} else if (lightType <= 0.3) {
vec3 projCoord = Shadow_GetCubeMapTC(lightVec.xyz);
float dist = sqrt(length(lightVec) * invRadius);
tc = vec4(projCoord.xy, shadowMapIndex + projCoord.z, dist);
} else {
projCoord = vProjCoord[startProjIndex];
projCoord.xyz /= projCoord.w;
startProjIndex += 1;
tc = g_ShadowMatrices[NUM_PSSM_SPLITS + lightIndex] * vec4(worldPos, 1.0);
tc.xyz /= tc.w;
tc = vec4(tc.xy, shadowMapIndex, tc.z);
}
return texture(g_ShadowMapArray, vec4(projCoord.xy, arraySlice, projCoord.z));
#if __VERSION__ >= 150
return texture(g_ShadowMapArray, tc);
#else
return shadow2DArray(g_ShadowMapArray, tc).x;
#endif
}
#endif
@ -149,26 +147,19 @@
}
#endif
#else
#define NUM_SHADOW_DIR_LIGHTS 0
#define NUM_SHADOW_POINT_LIGHTS 0
#define NUM_SHADOW_SPOT_LIGHTS 0
#define NUM_PSSM_SPLITS 0
const int pssmSliceOffset = 0;
void Shadow_ProcessProjCoord(vec3 worldPos) {
}
void Shadow_ProcessPssmSlice() {
}
float Shadow_ProcessDirectional(int startLightIndex, float startArrayLayer) {
return 1.0;
}
float Shadow_ProcessSpot(int startLightIndex, float startArrayLayer) {
return 1.0;
}
float Shadow_Process(in int lightType, in vec3 lightDir, in float startArrayLayer, inout int startProjIndex) {
float Shadow_Process(int lightIndex, float lightType, float shadowMapIndex,
vec3 lightVec, vec3 lightDir,
vec3 worldPos, float invRadius) {
return 1.0;
}
#endif

@ -7,7 +7,7 @@
* Outputs the light direction and the light half vector.
*/
void lightComputeDir(in vec3 worldPos, in float lightType, in vec4 position, out vec4 lightDir, out vec3 lightVec){
float posLight = step(0.5, lightType);
float posLight = step(0.2, lightType);
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
lightVec = tempVec;
float dist = length(tempVec);
@ -15,7 +15,7 @@ void lightComputeDir(in vec3 worldPos, in float lightType, in vec4 position, out
lightDir.w = (1.0 - position.w * dist) / (1.0 + position.w * dist * dist);
lightDir.w = clamp(lightDir.w, 1.0 - posLight, 1.0);
#else
lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
lightDir.w = 1.0; // clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
#endif
lightDir.xyz = tempVec / vec3(dist);
}

Loading…
Cancel
Save