不被雾影响的物体

缘起

场景中有一片浓雾,远处的物体看起来模糊不清。

问:能否让其中某物,不受雾的影响,不管多远仍然清晰可见?

该雾化场景的源码:HelloFog.java

此雾化特效的实现方式,是在整个场景渲染完毕后,在视口(ViewPort)上叠加一层雾化滤镜(FogFitler)。FogFilter的用途是根据已渲染过的画面上,根据像素到摄像机的距离来计算雾的颜色。

解决过程

方法1:多ViewPort覆盖(行不通)

思路:FogFitler是应用到ViewPort上的,只要通过这个ViewPort看到的物体,都会FogFilter影响。那么只要我们不通过这个ViewPort去观察物体,自然就不会受到FogFilter的影响了。

因此我需要创建一个新的ViewPort,把某个方块单独放在这个ViewPort中渲染,然后再把这个ViewPort中的画面和主ViewPort的画面叠加起来。

创建第二个Viewport的关键代码:

    // initialize the 2nd ViewPort
    ViewPort view = renderManager.createMainView("overlay", cam);//use the same viewport camera
    view.setClearFlags(false, false, false); // set it to not clear the colors/depth/stencil rendered by the first viewport
    view.attachScene(subscene);

源码:TestOverlayViewport.java

结果如图:

这个结果乍看起来是好的,但是..

由于第二个Viewport中的画面是直接叠加在主要Viewport上的,因此主要Viewport中的物体是挡不住第二个Viewport的。场景图中原本的遮挡关系被破坏了。

方法2:重写Shader,加入雾化功能

由于FogFilter是无差别地影响场景中的所有物体,所以没办法把某个物体独立出来。

片元着色器 Materials/Fog/Fog.frag

#import "Common/ShaderLib/GLSLCompat.glsllib"

uniform vec4 m_Color;
uniform vec4 m_FogColor;
uniform float m_FogDensity;

const float LOG2 = 1.442695;

void main() {

    gl_FragColor = m_Color;

#ifdef USE_FOG
    float dist = gl_FragCoord.z / gl_FragCoord.w;
    float fogFactor = exp2(-m_FogDensity * m_FogDensity * dist *      dist * LOG2 );
    gl_FragColor = mix(m_FogColor, gl_FragColor, fogFactor);
#endif

}

Material Def:

MaterialDef Fog{

    MaterialParameters {
        // For fog
        Boolean UseFog
        Color Color : 1.0 0.0 0.0 1.0 // red
        Color FogColor : 1.0 1.0 1.0 1.0 // white
        //Float FogDistance : 800.0
        Float FogDensity : 0.05
    }

    Technique {
        VertexShader GLSL100:   Common/MatDefs/Misc/Unshaded.vert
        FragmentShader GLSL100: Materials/Fog/Fog.frag

        WorldParameters {
            WorldViewProjectionMatrix
        }

        Defines {
            USE_FOG : UseFog
        }
    }
}

Java Code:

package net.jmecn.effect;

import com.jme3.app.SimpleApplication;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;

/**
 * Test Fog with shader
 * 
 * @author yanmaoyuan
 *
 */
public class TestFogShader extends SimpleApplication {

    @Override
    public void simpleInitApp() {

        createScene();

        addLights();

        flyCam.setMoveSpeed(10f);

        Material mat = new Material(assetManager, "Materials/Fog/Fog.j3md");
        mat.setColor("Color", ColorRGBA.Red);
        mat.setBoolean("UseFog", false);// disable the fog

        int size = rootNode.getChildren().size();
        Spatial divine = rootNode.getChild(size/2);
        divine.setMaterial(mat);
    }

    private void createScene() {
        Material mat = new Material(assetManager, "Materials/Fog/Fog.j3md");
        mat.setColor("Color", ColorRGBA.Green);
        mat.setFloat("FogDensity", 0.03f);
        mat.setBoolean("UseFog", true);// Let the fog work

        Geometry geom = new Geometry("Floor", new Quad(40, 40));
        geom.setMaterial(mat);
        geom.rotate(-FastMath.HALF_PI, 0, 0);
        rootNode.attachChild(geom);

        for (int y = 0; y < 33; y += 4) {
            for (int x = 0; x < 33; x += 4) {
                geom = new Geometry("Cube", new Box(0.5f, 0.5f, 0.5f));
                geom.setMaterial(mat);
                geom.move(x + 4, 0.5f, -y - 4);
                rootNode.attachChild(geom);
            }
        }
    }

    private void addLights() {
        DirectionalLight sunLight = new DirectionalLight();
        sunLight.setDirection(new Vector3f(-1, -2, -3).normalizeLocal());
        sunLight.setColor(new ColorRGBA(0.8f, 0.8f, 0.8f, 1f));

        AmbientLight ambientLight = new AmbientLight();
        ambientLight.setColor(new ColorRGBA(0.2f, 0.2f, 0.2f, 1f));

        rootNode.addLight(sunLight);
        rootNode.addLight(ambientLight);
    }

    public static void main(String[] args) {
        TestFogShader app = new TestFogShader();
        app.start();
    }

}

方法3:ShaderNode

https://github.com/jMonkeyEngine/jmonkeyengine/tree/master/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Fog

http://jmonkeyengine.github.io/wiki/jme3/advanced/jme3_shadernodes.html