• libgdx 3D 渲染优化


    原文地址:http://blog.xoppa.com/3d-frustum-culling-with-libgdx/

    非翻译,详细内容请见原文,捡点我能理解的说说吧~

    关于矩阵之类的知识可以看一下 http://www.cppblog.com/lovedday/archive/2008/01/09/40813.html 我也不懂~~

    渲染3D场景的时候,可见物件的数量要远远小于物件总数。

    试想,在铺了一地地砖的时候,视角对着天,这时候需要渲染的地砖就没有了。摄像机照不到的地方,完全没有必要渲染。

    只渲染可视视角范围内的物件,对于性能的提升是很大的。

    直接上测试代码:

      1 package com.mygdx.game;
      2 
      3 import com.badlogic.gdx.ApplicationListener;
      4 import com.badlogic.gdx.Gdx;
      5 import com.badlogic.gdx.assets.AssetManager;
      6 import com.badlogic.gdx.graphics.Camera;
      7 import com.badlogic.gdx.graphics.Color;
      8 import com.badlogic.gdx.graphics.GL20;
      9 import com.badlogic.gdx.graphics.PerspectiveCamera;
     10 import com.badlogic.gdx.graphics.g2d.BitmapFont;
     11 import com.badlogic.gdx.graphics.g3d.Environment;
     12 import com.badlogic.gdx.graphics.g3d.Model;
     13 import com.badlogic.gdx.graphics.g3d.ModelBatch;
     14 import com.badlogic.gdx.graphics.g3d.ModelInstance;
     15 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
     16 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
     17 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
     18 import com.badlogic.gdx.math.Vector3;
     19 import com.badlogic.gdx.math.collision.BoundingBox;
     20 import com.badlogic.gdx.scenes.scene2d.Stage;
     21 import com.badlogic.gdx.scenes.scene2d.ui.Label;
     22 import com.badlogic.gdx.utils.Array;
     23 
     24 /**
     25  * Created by HanHongmin on 14-7-24.
     26  */
     27 public class FrustumCullingTestA implements ApplicationListener {
     28     protected PerspectiveCamera cam;
     29     protected CameraInputController camController;
     30     protected ModelBatch modelBatch;
     31     protected AssetManager assets;
     32     protected Array<ModelInstance> instances = new Array();
     33     protected Environment environment;
     34     protected boolean loading;
     35 
     36     protected Stage stage;
     37     protected Label label;
     38     protected BitmapFont font;
     39     protected StringBuilder stringBuilder;
     40     private Vector3 tempPosition = new Vector3();
     41 
     42     @Override
     43     public void create () {
     44         stage = new Stage();
     45         font = new BitmapFont();
     46         label = new Label(" ", new Label.LabelStyle(font, Color.WHITE));
     47         stage.addActor(label);
     48         stringBuilder = new StringBuilder();
     49 
     50         modelBatch = new ModelBatch();
     51         environment = new Environment();
     52         environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
     53         environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
     54 
     55         cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
     56         cam.position.set(0f, 7f, 10f);
     57         cam.lookAt(0,0,0);
     58         cam.near = 1f;
     59         cam.far = 300f;
     60         cam.update();
     61 
     62         camController = new CameraInputController(cam);
     63         Gdx.input.setInputProcessor(camController);
     64 
     65         assets = new AssetManager();
     66         assets.load("data/box.g3db", Model.class);
     67         assets.load("data/box2.g3db", Model.class);
     68         loading = true;
     69     }
     70 
     71     private void doneLoading() {
     72         Model t1 = assets.get("data/box.g3db", Model.class);
     73         Model t2 = assets.get("data/box2.g3db", Model.class);
     74 
     75         for(int i=0;i<100;i++){
     76             for(int j=0;j<100;j++){
     77                 Model t;
     78                 if((i+j)%2==0){
     79                     t = t1;
     80                 }else{
     81                     t=t2;
     82                 }
     83                 ModelInstance shipInstance = new ModelInstance(t);
     84                 shipInstance.transform.setToTranslation(i, j, 0);//设置位置
     85                 instances.add(shipInstance);
     86             }
     87         }
     88 
     89         loading = false;
     90     }
     91 
     92     private int visibleCount;
     93     @Override
     94     public void render () {
     95         if (loading && assets.update())
     96             doneLoading();
     97         camController.update();
     98 
     99         Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    100         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
    101 
    102         modelBatch.begin(cam);
    103         visibleCount = 0;
    104         for (final ModelInstance instance : instances) {
    105             if (isVisible(cam, instance)) {
    106                 modelBatch.render(instance, environment);
    107                 visibleCount++;
    108             }
    109         }
    110         modelBatch.end();
    111 
    112         stringBuilder.setLength(0);
    113         stringBuilder.append(" FPS: ").append(Gdx.graphics.getFramesPerSecond());
    114         stringBuilder.append(" Visible: ").append(visibleCount);
    115         label.setText(stringBuilder);
    116         stage.draw();
    117     }
    118 
    119     protected boolean isVisible(final Camera cam, final ModelInstance instance) {
    120         instance.transform.getTranslation(tempPosition);//把位置信息放到tempPosition中
    121         return cam.frustum.pointInFrustum(tempPosition);//frustum平截头体,表示的就是可视范围
    122     }
    123 
    124     @Override
    125     public void dispose () {
    126         modelBatch.dispose();
    127         instances.clear();
    128         assets.dispose();
    129     }
    130 
    131     @Override
    132     public void resize(int width, int height) {
    133         stage.getViewport().update(width, height, true);
    134     }
    135 
    136     @Override
    137     public void pause() {
    138     }
    139 
    140     @Override
    141     public void resume() {
    142     }
    143 }
    View Code

    以上代码中,关键部分是isVisible函数,该函数中计算了物件位置是否是在可视范围内。

    这种算法稍稍有点瑕疵,运行即可看出效果。如果一个物件的位置不在可视范围内,但是此物件有一角却在,常理来说是应该显示的,而实际却没有显示出来。

    上代码吧:

      1 package com.mygdx.game;
      2 
      3 import com.badlogic.gdx.ApplicationListener;
      4 import com.badlogic.gdx.Gdx;
      5 import com.badlogic.gdx.assets.AssetManager;
      6 import com.badlogic.gdx.graphics.Camera;
      7 import com.badlogic.gdx.graphics.Color;
      8 import com.badlogic.gdx.graphics.GL20;
      9 import com.badlogic.gdx.graphics.PerspectiveCamera;
     10 import com.badlogic.gdx.graphics.g2d.BitmapFont;
     11 import com.badlogic.gdx.graphics.g3d.Environment;
     12 import com.badlogic.gdx.graphics.g3d.Model;
     13 import com.badlogic.gdx.graphics.g3d.ModelBatch;
     14 import com.badlogic.gdx.graphics.g3d.ModelInstance;
     15 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
     16 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
     17 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
     18 import com.badlogic.gdx.math.Vector3;
     19 import com.badlogic.gdx.math.collision.BoundingBox;
     20 import com.badlogic.gdx.scenes.scene2d.Stage;
     21 import com.badlogic.gdx.scenes.scene2d.ui.Label;
     22 import com.badlogic.gdx.utils.Array;
     23 
     24 /**
     25  * Created by HanHongmin on 14-7-24.
     26  */
     27 public class FrustumCullingTest implements ApplicationListener {
     28     protected PerspectiveCamera cam;
     29     protected CameraInputController camController;
     30     protected ModelBatch modelBatch;
     31     protected AssetManager assets;
     32     protected Array<GameObject> instances = new Array();
     33     protected Environment environment;
     34     protected boolean loading;
     35 
     36     protected Stage stage;
     37     protected Label label;
     38     protected BitmapFont font;
     39     protected StringBuilder stringBuilder;
     40     private Vector3 tempPosition = new Vector3();
     41 
     42     @Override
     43     public void create () {
     44         stage = new Stage();
     45         font = new BitmapFont();
     46         label = new Label(" ", new Label.LabelStyle(font, Color.WHITE));
     47         stage.addActor(label);
     48         stringBuilder = new StringBuilder();
     49 
     50         modelBatch = new ModelBatch();
     51         environment = new Environment();
     52         environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
     53         environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
     54 
     55         cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
     56         cam.position.set(0f, 7f, 10f);
     57         cam.lookAt(0,0,0);
     58         cam.near = 1f;
     59         cam.far = 30f;//视角最远
     60         cam.update();
     61 
     62         camController = new CameraInputController(cam);
     63         Gdx.input.setInputProcessor(camController);
     64 
     65         assets = new AssetManager();
     66         assets.load("data/box.g3db", Model.class);
     67         assets.load("data/box2.g3db", Model.class);
     68         loading = true;
     69     }
     70 
     71     private void doneLoading() {
     72         Model t1 = assets.get("data/box.g3db", Model.class);
     73         Model t2 = assets.get("data/box2.g3db", Model.class);
     74 
     75         for(int i=0;i<100;i++){
     76             for(int j=0;j<100;j++){
     77                 Model t;
     78                 if((i+j)%2==0){
     79                     t = t1;
     80                 }else{
     81                     t=t2;
     82                 }
     83                 GameObject shipInstance = new GameObject(t);
     84                 shipInstance.transform.setToTranslation(i, j, 0);//设置位置
     85                 instances.add(shipInstance);
     86             }
     87         }
     88 
     89         loading = false;
     90     }
     91 
     92     private int visibleCount;
     93     @Override
     94     public void render () {
     95         if (loading && assets.update())
     96             doneLoading();
     97         camController.update();
     98 
     99         Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    100         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
    101 
    102         modelBatch.begin(cam);
    103         visibleCount = 0;
    104         for (final GameObject instance : instances) {
    105             if (isVisible(cam, instance)) {
    106                 modelBatch.render(instance, environment);
    107                 visibleCount++;
    108             }
    109         }
    110         modelBatch.end();
    111 
    112         stringBuilder.setLength(0);
    113         stringBuilder.append(" FPS: ").append(Gdx.graphics.getFramesPerSecond());
    114         stringBuilder.append(" Visible: ").append(visibleCount);
    115         label.setText(stringBuilder);
    116         stage.draw();
    117     }
    118 
    119     protected boolean isVisible(final Camera cam, final GameObject instance) {
    120         instance.transform.getTranslation(tempPosition);
    121         tempPosition.add(instance.center);
    122         return cam.frustum.boundsInFrustum(tempPosition, instance.dimensions);
    123     }
    124 
    125     @Override
    126     public void dispose () {
    127         modelBatch.dispose();
    128         instances.clear();
    129         assets.dispose();
    130     }
    131 
    132     @Override
    133     public void resize(int width, int height) {
    134         stage.getViewport().update(width, height, true);
    135     }
    136 
    137     @Override
    138     public void pause() {
    139     }
    140 
    141     @Override
    142     public void resume() {
    143     }
    144 
    145 
    146 
    147     public static class GameObject extends ModelInstance {
    148         public final Vector3 center = new Vector3();
    149         public final Vector3 dimensions = new Vector3();
    150 
    151         private final static BoundingBox bounds = new BoundingBox();
    152 
    153         public GameObject(Model model) {
    154             super(model);
    155             calculateBoundingBox(bounds);
    156             center.set(bounds.getCenter());
    157             dimensions.set(bounds.getDimensions());
    158         }
    159     }
    160 }

    这个怎么算的,其实我想说:呵呵!我也不懂!!!谁整明白了请告诉我啊~

    估计是看见一点就显示吧。反正是打到我们想要的效果了。

    原文作者提到,当物件旋转时,可能会出现问题。贴一下原作最后的代码:

      1 package com.mygdx.game;
      2 
      3 import com.badlogic.gdx.ApplicationListener;
      4 import com.badlogic.gdx.Gdx;
      5 import com.badlogic.gdx.assets.AssetManager;
      6 import com.badlogic.gdx.graphics.Camera;
      7 import com.badlogic.gdx.graphics.Color;
      8 import com.badlogic.gdx.graphics.GL20;
      9 import com.badlogic.gdx.graphics.PerspectiveCamera;
     10 import com.badlogic.gdx.graphics.g2d.BitmapFont;
     11 import com.badlogic.gdx.graphics.g3d.Environment;
     12 import com.badlogic.gdx.graphics.g3d.Model;
     13 import com.badlogic.gdx.graphics.g3d.ModelBatch;
     14 import com.badlogic.gdx.graphics.g3d.ModelInstance;
     15 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
     16 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
     17 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
     18 import com.badlogic.gdx.math.Vector3;
     19 import com.badlogic.gdx.math.collision.BoundingBox;
     20 import com.badlogic.gdx.scenes.scene2d.Stage;
     21 import com.badlogic.gdx.scenes.scene2d.ui.Label;
     22 import com.badlogic.gdx.utils.Array;
     23 
     24 /**
     25  * Created by HanHongmin on 14-7-24.
     26  */
     27 public class FrustumCullingTestC implements ApplicationListener {
     28     protected PerspectiveCamera cam;
     29     protected CameraInputController camController;
     30     protected ModelBatch modelBatch;
     31     protected AssetManager assets;
     32     protected Array<GameObject> instances = new Array();
     33     protected Environment environment;
     34     protected boolean loading;
     35 
     36     protected Stage stage;
     37     protected Label label;
     38     protected BitmapFont font;
     39     protected StringBuilder stringBuilder;
     40     private Vector3 tempPosition = new Vector3();
     41 
     42     @Override
     43     public void create () {
     44         stage = new Stage();
     45         font = new BitmapFont();
     46         label = new Label(" ", new Label.LabelStyle(font, Color.WHITE));
     47         stage.addActor(label);
     48         stringBuilder = new StringBuilder();
     49 
     50         modelBatch = new ModelBatch();
     51         environment = new Environment();
     52         environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
     53         environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
     54 
     55         cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
     56         cam.position.set(0f, 7f, 10f);
     57         cam.lookAt(0,0,0);
     58         cam.near = 1f;
     59         cam.far = 30f;//视角最远
     60         cam.update();
     61 
     62         camController = new CameraInputController(cam);
     63         Gdx.input.setInputProcessor(camController);
     64 
     65         assets = new AssetManager();
     66         assets.load("data/box.g3db", Model.class);
     67         assets.load("data/box2.g3db", Model.class);
     68         loading = true;
     69     }
     70 
     71     private void doneLoading() {
     72         Model t1 = assets.get("data/box.g3db", Model.class);
     73         Model t2 = assets.get("data/box2.g3db", Model.class);
     74 
     75         for(int i=0;i<100;i++){
     76             for(int j=0;j<100;j++){
     77                 Model t;
     78                 if((i+j)%2==0){
     79                     t = t1;
     80                 }else{
     81                     t=t2;
     82                 }
     83                 GameObject shipInstance = new GameObject(t);
     84                 shipInstance.transform.setToTranslation(i, j, 0);//设置位置
     85                 instances.add(shipInstance);
     86             }
     87         }
     88 
     89         loading = false;
     90     }
     91 
     92     private int visibleCount;
     93     @Override
     94     public void render () {
     95         if (loading && assets.update())
     96             doneLoading();
     97         camController.update();
     98 
     99         Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    100         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
    101 
    102         modelBatch.begin(cam);
    103         visibleCount = 0;
    104         for (final GameObject instance : instances) {
    105             if (isVisible(cam, instance)) {
    106                 modelBatch.render(instance, environment);
    107                 visibleCount++;
    108             }
    109         }
    110         modelBatch.end();
    111 
    112         stringBuilder.setLength(0);
    113         stringBuilder.append(" FPS: ").append(Gdx.graphics.getFramesPerSecond());
    114         stringBuilder.append(" Visible: ").append(visibleCount);
    115         label.setText(stringBuilder);
    116         stage.draw();
    117     }
    118 
    119     protected boolean isVisible(final Camera cam, final GameObject instance) {
    120         instance.transform.getTranslation(tempPosition);
    121         tempPosition.add(instance.center);
    122         return cam.frustum.sphereInFrustum(tempPosition, instance.radius);
    123     }
    124 
    125     @Override
    126     public void dispose () {
    127         modelBatch.dispose();
    128         instances.clear();
    129         assets.dispose();
    130     }
    131 
    132     @Override
    133     public void resize(int width, int height) {
    134         stage.getViewport().update(width, height, true);
    135     }
    136 
    137     @Override
    138     public void pause() {
    139     }
    140 
    141     @Override
    142     public void resume() {
    143     }
    144 
    145 
    146 
    147     public static class GameObject extends ModelInstance {
    148         public final Vector3 center = new Vector3();
    149         public final Vector3 dimensions = new Vector3();
    150         public final float radius;
    151 
    152         private final static BoundingBox bounds = new BoundingBox();
    153 
    154         public GameObject(Model model) {
    155             super(model);
    156             calculateBoundingBox(bounds);
    157             center.set(bounds.getCenter());
    158             dimensions.set(bounds.getDimensions());
    159             radius = dimensions.len() / 2f;
    160         }
    161     }
    162 }

    注:以上代码我都有改过,比如GameObject的构造方法。会不会有问题我真不清楚。

    另外还有两点:

    1. Camera的far属性能够影响可视范围,不要忘了。

    2. far边缘像是被刀切了一样,一条线,far越近越明显,是否有办法根据物件和摄像机位置再次筛选是否渲染呢?

    far是一条直线,摄像机正对的中点看得更远一下可能更真实。

  • 相关阅读:
    第1次实践作业
    Beta版本演示
    2019 SDN上机第7次作业
    Beta冲刺(4/4)
    Beta冲刺(3/4)
    Beta冲刺(2/4)
    Beta冲刺(1/4)
    2019 SDN上机第6次作业
    2019 SDN上机第5次作业
    SDN课程阅读作业(2)
  • 原文地址:https://www.cnblogs.com/hanhongmin/p/3866305.html
Copyright © 2020-2023  润新知