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是一条直线,摄像机正对的中点看得更远一下可能更真实。

原文地址:https://www.cnblogs.com/hanhongmin/p/3866305.html