diff --git a/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java index ad6a6f72e..873029d99 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java @@ -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 { @@ -84,6 +85,28 @@ public class DefaultTechniqueDefLogic implements TechniqueDefLogic { renderManager.getLightFilter().filterLights(geom, filteredLightList); 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); diff --git a/jme3-core/src/main/java/com/jme3/material/logic/ShadowStaticPassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/ShadowStaticPassLightingLogic.java index c5c11c32d..04bdc226f 100755 --- a/jme3-core/src/main/java/com/jme3/material/logic/ShadowStaticPassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/ShadowStaticPassLightingLogic.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java index 2a42a8440..23970f95f 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java @@ -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 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,16 +97,52 @@ 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); - } else { - defines.set(indirectLightingDefineId, true); + 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() { + @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 { + 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++) { - - - Light l = lightList.get(curIndex); - if(l.getType() == Light.Type.Ambient){ - endIndex++; - continue; + int endIndex = Math.min(startIndex + numLights, lightList.size()); + + ArrayShadowMap directionalShadowMap = null; + + for (curIndex = startIndex; curIndex < endIndex; curIndex++) { + Light light = lightList.get(curIndex); + + 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(), - color.getGreen(), - color.getBlue(), - l.getType().getId(), - lightDataIndex); - lightDataIndex++; + + 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(), + 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()); } } - vars.release(); - //Padding of unsued buffer space - while(lightDataIndex < numLights * 3) { - lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex); - lightDataIndex++; + // 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); + } + + 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; - } } diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java index 469e7a73d..c901c8f81 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java @@ -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++; diff --git a/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java index 630814e37..6ba92e10b 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java @@ -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) { diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowArrayRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowArrayRenderer.java index 07cc74eed..06d3eca5d 100755 --- a/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowArrayRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowArrayRenderer.java @@ -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,12 +107,14 @@ 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) { @@ -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, diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java new file mode 100755 index 000000000..ef05b1dcd --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java @@ -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. + *

+ * 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 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) { + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java new file mode 100644 index 000000000..45db107d5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java @@ -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 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) { + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java index 7e22ad05b..5cbd37396 100755 --- a/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java @@ -82,6 +82,7 @@ public class BaseArrayShadowMapSlice implements ArrayShadowMapS if (fbNeedClear) { renderer.setFrameBuffer(frameBuffer); + renderer.clearClipRect(); renderer.clearBuffers(false, true, false); fbNeedClear = false; } diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java index 06b8f0a68..91d46fcd3 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java @@ -51,7 +51,7 @@ public class PointArrayShadowMapSlice extends BaseArrayShadowMapSlice public SpotArrayShadowMapSlice(TextureArray array, int layer, int textureSize) { 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()); - shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Y); + 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); diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java new file mode 100755 index 000000000..e9b999fbe --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java @@ -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 implements ShadowMapSlice { + + 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(); + } +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java new file mode 100755 index 000000000..bc30b9aff --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java @@ -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 { + + 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; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java new file mode 100755 index 000000000..494dec1c2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java @@ -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 { + + 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; + } +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag index 0f5225f39..b6581888b 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag @@ -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,11 +218,20 @@ 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]; - vec4 lightData1 = g_LightData[i+1]; + + 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; lightComputeDir(wPosition, lightColor.w, lightData1, lightDir, 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/ diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert index 77782456b..99c50564f 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert @@ -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) diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag index f0f76e70c..7366a60d8 100755 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag @@ -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; + vec4 lightData1 = g_LightData[lightIndex + 1]; + float shadowMapIndex = -1.0; - PBR_ComputeDirectLightSpecWF(surface.normal, -lightDirection, surface.viewDir, - lightColor.rgb, surface.specular.rgb, surface.roughness, surface.ndotv, - outDiffuse, outSpecular); -} - -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 } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert index f8ca36fae..c62d50488 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert @@ -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; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag new file mode 100644 index 000000000..ad44a891d --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag @@ -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; +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md new file mode 100644 index 000000000..6c89f2043 --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md @@ -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 + } + } +} diff --git a/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl b/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl index 05e3955c3..3cf681f84 100755 --- a/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl +++ b/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl @@ -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); - } - - 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)); - } - - 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)); + 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); + } + } 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); + } + } + 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; - - 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; + float Shadow_Process(int lightIndex, float lightType, float shadowMapIndex, + vec3 lightVec, vec3 lightDir, + vec3 worldPos, float invRadius) { + vec4 tc; + + 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,10 +147,9 @@ } #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) { } @@ -160,15 +157,9 @@ 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 diff --git a/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib index d75f4eb9c..f5536afe5 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib @@ -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); }