高清环境贴图

最近认识了一个网站,作者无私分享高清HDR环境贴图,下载之前无需注册。

网址是:https://hdrihaven.com/

点击页头菜单中的"HDRIs",就可以看到各种环境贴图的类型,包含户外、室内、天空等等。

我在户外(Outdoor)分类下,随意点开一个名为 Tiergarten 的环境贴图,页面上出现了它的预览图。

把这个页面往下拉,中间出现了使用这个环境贴图的PBR材质球;左下提供了不同分辨率下载链接,并显示了文件的体积;右下显示了这个文件的信息。

我下载了4K分辨率(4069x2048)的贴图,得到了这样一个文件:tiergarten_4k.hdr。也许我可以在jMonkeyEngine中测试一下它的效果。

用Eclipse创建一个gradle项目,build.gradle脚本如下:

apply plugin: 'java-library'

repositories {
    jcenter()
}

dependencies {
    def jme3 = [g:'org.jmonkeyengine', v: '3.2.0-beta1']
    // 编译环境
    compile "$jme3.g:jme3-core:$jme3.v"
    // 桌面运行环境
    runtime "$jme3.g:jme3-desktop:$jme3.v"
    runtime "$jme3.g:jme3-lwjgl3:$jme3.v"
}

Eclipse 创建的 Gradle 工程默认是没有创建资源目录。手动在src/main目录下创建 resources 文件夹,然后执行一下 build.gradle 脚本。Gradle工程会自动识别resources 文件夹,并把它作为加载资源文件的 classpath。

在resources目录下创建 Textures/Sky 目录,并把 tiergarten_4k.hdr 复制进去,这样在程序里就可以通过路径 Textures/Sky/tiergarten_4k.hdr 加载这个环境贴图了。

随后创建 net.jmecn.TestEnvMap 类,用它来测试HDR环境贴图。

package net.jmecn;

import com.jme3.app.SimpleApplication;
import com.jme3.scene.Spatial;
import com.jme3.util.SkyFactory;
import com.jme3.util.SkyFactory.EnvMapType;

/**
 * 测试HDR环境贴图
 * @author yanmaoyuan
 *
 */
public class TestEnvMap extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        // 加载环境贴图,生成天空盒。
        Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/tiergarten_4k.hdr", EnvMapType.EquirectMap);
        rootNode.attachChild(sky);
    }

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

}

测试代码非常简单,运行一下看看效果。

画面是这样的:

画面挺暗,跟网站预览的不太一样,估计是颜色空间(ColorSpace)不对。查看一下控制台的输出,果然:

十二月 26, 2017 9:43:01 下午 com.jme3.material.Material checkTextureParamColorSpace
警告: The texture Textures/Sky/tiergarten_4k.hdr has linear color space, but the material parameter Texture specifies no color space requirement, this may lead to unexpected behavior.
Check if the image was not set to another material parameter with a linear color space, or that you did not set the ColorSpace to Linear using texture.getImage.setColorSpace().

“警告”提示这个纹理使用的是线性颜色空间(linear color space)。需要开启“伽马校正”才能显示正常亮度。

再次运行测试程序,注意在设置窗口中勾选“伽马校正”,然后看到画面是这样的:

这下亮多了。

不过,动态高光(HDR)贴图的特点就是亮的地方特别亮,会给人过度曝光的感觉,需要使用曝光镜头来平衡不同区域的亮度。

jme3-effects 模块中有一个名为 ToneMapFilter 的后处理滤镜,专门用来处理曝光白平衡。使用它需要在 build.gradle 脚本中添加一行依赖。

    compile "$jme3.g:jme3-effects:$jme3.v"

执行 build.gradle ,让工程自动下载所需的依赖。随后改一下代码,添加曝光滤镜。

package net.jmecn;

import com.jme3.app.SimpleApplication;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.ToneMapFilter;
import com.jme3.scene.Spatial;
import com.jme3.util.SkyFactory;
import com.jme3.util.SkyFactory.EnvMapType;

/**
 * 测试HDR环境贴图
 * @author yanmaoyuan
 *
 */
public class TestEnvMap extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        // 加载环境贴图,生成天空盒。
        Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/tiergarten_4k.hdr", EnvMapType.EquirectMap);
        rootNode.attachChild(sky);
        
        // 开启曝光滤镜
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        ToneMapFilter toneMapFilter = new ToneMapFilter();
        fpp.addFilter(toneMapFilter);
        viewPort.addProcessor(fpp);
    }

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

}

再次运行程序,画面是这样的。

对比:

可以看到,使用了曝光滤镜的画面确实有变化,画面整体感觉暗了一些。

曝光滤镜其实并不该用在这里,它其实是配合更加复杂的场景使用的。比如人从比较暗的室内来到亮度更大的室外,通常会觉得外面比较刺眼,需要一点时间来适应。这时候瞳孔会缩小,以减少视网膜感受到的光亮度。

曝光滤镜实际上就是模拟这个过程。在这个测试程序中,实际上并没有什么较暗的室内场景,所以也看不出来区别。

好了,最后确认 hdrihaven.com 这个网站提供的HDR环境贴图是可用的,可喜可贺!

感谢作者的分享!