Point Of Interest light probe blending start of implementation : basic structure, probes selection and blend factor computation.

define_list_fix
Nehon 9 years ago
parent 9fd90ab799
commit 1276dc583e
  1. 2
      jme3-core/src/main/java/com/jme3/light/LightProbe.java
  2. 208
      jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java
  3. 107
      jme3-core/src/main/java/com/jme3/light/PoiLightProbeLightFilter.java
  4. 9
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java

@ -253,7 +253,7 @@ public class LightProbe extends Light implements Savable {
@Override
public String toString() {
return "Light Probe : " + position ;
return "Light Probe : " + name + " at " + position + " / " + bounds;
}
@Override

@ -0,0 +1,208 @@
/*
* Copyright (c) 2009-2015 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.light;
import com.jme3.bounding.BoundingSphere;
import com.jme3.post.SceneProcessor;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Spatial;
import com.jme3.texture.FrameBuffer;
import com.jme3.util.TempVars;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* this processor allows to blend several light probes maps together according to a Point of Interest.
* This is all based on this article by Sebastien lagarde
* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
* @author Nehon
*/
public class LightProbeBlendingProcessor implements SceneProcessor {
private ViewPort viewPort;
private LightFilter prevFilter;
private RenderManager renderManager;
private LightProbe probe = new LightProbe();
private Spatial poi;
public LightProbeBlendingProcessor(Spatial poi) {
this.poi = poi;
}
@Override
public void initialize(RenderManager rm, ViewPort vp) {
viewPort = vp;
renderManager = rm;
prevFilter = rm.getLightFilter();
rm.setLightFilter(new PoiLightProbeLightFilter(this));
}
@Override
public void reshape(ViewPort vp, int w, int h) {
}
@Override
public boolean isInitialized() {
return viewPort != null;
}
@Override
public void preFrame(float tpf) {
}
/** 1. For POI take a spatial in the constructor and make all calculation against its world pos
* - Alternatively compute an arbitrary POI by casting rays from the camera
* (one in the center and one for each corner and take the median point)
* 2. Take the 4 most weighted probes for default. Maybe allow the user to change this
* 3. For the inner influence radius take half of the radius for a start we'll see then how to change this.
*
*/
@Override
public void postQueue(RenderQueue rq) {
List<BlendFactor> blendFactors = new ArrayList<BlendFactor>();
float sumBlendFactors = computeBlendFactors(blendFactors);
//Sort blend factors according to their weight
Collections.sort(blendFactors);
//normalize blend factors;
float normalizer = 1f / sumBlendFactors;
for (BlendFactor blendFactor : blendFactors) {
blendFactor.ndf *= normalizer;
// System.err.println(blendFactor);
}
//for now just pick the first probe.
if(!blendFactors.isEmpty()){
probe = blendFactors.get(0).lightProbe;
}else{
probe = null;
}
}
private float computeBlendFactors(List<BlendFactor> blendFactors) {
float sumBlendFactors = 0;
for (Spatial scene : viewPort.getScenes()) {
for (Light light : scene.getWorldLightList()) {
if(light.getType() == Light.Type.Probe){
LightProbe p = (LightProbe)light;
TempVars vars = TempVars.get();
boolean intersect = p.intersectsFrustum(viewPort.getCamera(), vars);
vars.release();
//check if the probe is inside the camera frustum
if(intersect){
//is the poi inside the bounds of this probe
if(poi.getWorldBound().intersects(p.getBounds())){
//computing the distance as we need it to check if th epoi in in the inner radius and later to compute the weight
float outerRadius = ((BoundingSphere)p.getBounds()).getRadius();
float innerRadius = outerRadius * 0.5f;
float distance = p.getBounds().getCenter().distance(poi.getWorldTranslation());
// if the poi in inside the inner range of this probe, then this probe is the only one that matters.
if( distance < innerRadius ){
blendFactors.clear();
blendFactors.add(new BlendFactor(p, 1.0f));
return 1.0f;
}
//else we need to compute the weight of this probe and collect it for blending
float ndf = (distance - innerRadius) / (outerRadius - innerRadius);
sumBlendFactors += ndf;
blendFactors.add(new BlendFactor(p, ndf));
}
}
}
}
}
return sumBlendFactors;
}
@Override
public void postFrame(FrameBuffer out) {
}
@Override
public void cleanup() {
viewPort = null;
renderManager.setLightFilter(prevFilter);
}
public void populateProbe(LightList lightList){
if(probe != null && probe.isReady()){
lightList.add(probe);
}
}
public Spatial getPoi() {
return poi;
}
public void setPoi(Spatial poi) {
this.poi = poi;
}
private class BlendFactor implements Comparable<BlendFactor>{
LightProbe lightProbe;
float ndf;
public BlendFactor(LightProbe lightProbe, float ndf) {
this.lightProbe = lightProbe;
this.ndf = ndf;
}
@Override
public String toString() {
return "BlendFactor{" + "lightProbe=" + lightProbe + ", ndf=" + ndf + '}';
}
@Override
public int compareTo(BlendFactor o) {
if(o.ndf > ndf){
return -1;
}else if(o.ndf < ndf){
return 1;
}
return 0;
}
}
}

@ -0,0 +1,107 @@
/*
* Copyright (c) 2009-2015 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.light;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.util.TempVars;
import java.util.HashSet;
public final class PoiLightProbeLightFilter implements LightFilter {
private Camera camera;
private final HashSet<Light> processedLights = new HashSet<Light>();
private final LightProbeBlendingProcessor processor;
public PoiLightProbeLightFilter(LightProbeBlendingProcessor processor) {
this.processor = processor;
}
@Override
public void setCamera(Camera camera) {
this.camera = camera;
for (Light light : processedLights) {
light.frustumCheckNeeded = true;
}
}
@Override
public void filterLights(Geometry geometry, LightList filteredLightList) {
TempVars vars = TempVars.get();
try {
LightList worldLights = geometry.getWorldLightList();
for (int i = 0; i < worldLights.size(); i++) {
Light light = worldLights.get(i);
if (light.getType() == Light.Type.Probe) {
continue;
}
if (light.frustumCheckNeeded) {
processedLights.add(light);
light.frustumCheckNeeded = false;
light.intersectsFrustum = light.intersectsFrustum(camera, vars);
}
if (!light.intersectsFrustum) {
continue;
}
BoundingVolume bv = geometry.getWorldBound();
if (bv instanceof BoundingBox) {
if (!light.intersectsBox((BoundingBox) bv, vars)) {
continue;
}
} else if (bv instanceof BoundingSphere) {
if (!Float.isInfinite(((BoundingSphere) bv).getRadius())) {
if (!light.intersectsSphere((BoundingSphere) bv, vars)) {
continue;
}
}
}
filteredLightList.add(light);
}
processor.populateProbe(filteredLightList);
} finally {
vars.release();
}
}
}

@ -754,6 +754,15 @@ public class RenderManager {
public void setLightFilter(LightFilter lightFilter) {
this.lightFilter = lightFilter;
}
/**
* Returns the current LightFilter.
*
* @return the current light filter
*/
public LightFilter getLightFilter() {
return this.lightFilter;
}
/**
* Defines what light mode will be selected when a technique offers several light modes.

Loading…
Cancel
Save