libGDX:灯光效果实现一(实现一个点光源)

国内的libGDX文章很少,特别是libGDX实现灯光效果,所以就开始总结灯光效果的实现

1.Box2dLights库功能简介

库版本:1.5

目前Box2dLights提供了以下几种灯光效果模拟

  1. PointLight:点光源,就是中心点发光,向周围辐射,所以这个光源模拟的效果总是圆形的
  2. ConeLight:圆锥光源,也是从一点发光,但是光照角度和范围可以控制,像手电筒或投影仪那种
  3. DirectionalLight:模拟无限远的光源,光照效果不会随着距离和方向变弱,所以经常用来模拟阳光
  4. ChainLight:链式灯光,我们可以通过定义顶点更灵活的控制灯光模拟形状(比如让一个自定义的Body周围发光)

绿色的框 是为了方便看到Body位置,使用Box2DDebugRenderer渲染的

工欲善其事,必先利其器,工具集合
gdx-setup.jar

1. 从libGDX官网下载项目生成工具

https://libgdx.com/wiki/start/setup

2.配置项目

setup配置.png

Output folder和Android SDK设置自己的,扩展库选择Box2dBox2dlights,然后生成项目

3.配置ApplicationConfiguration,初始化ApplicationListener

由于是演示项目,所以直接使用系统创建好的Activity实现类

public class AndroidLauncher extends AndroidApplication {
   @Override
   protected void onCreate (Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
      config.a = 8;
      config.r = 8;
      config.g = 8;
      config.b = 8;
      initialize(new PointLightTest(), config);
   }
}

initialize的第一个参数后面的每个例子都会改成需要的那个类

4.创建一个PointLight

点光源.png

public class PointLightTest implements ApplicationListener {
   /** the camera **/
   OrthographicCamera camera;
   RayHandler rayHandler;
   World world;

   @Override
   public void create() {
      // 1. 创建正交投影相机,这里设置的视口直接就是屏幕大小
      camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
      // 2.把相机放到屏幕中心 这两步是为了更直观的看到映射后的效果,不用去计算灯光位置什么
      // 的,都放到中心显示
      camera.position.set(Gdx.graphics.getWidth() / 2f,Gdx.graphics.getHeight() / 2f, 0);
      // 3.更新相机
      camera.update();
      // 4.创建物理世界
      world = new World(new Vector2(0, -10), true);
      // 5.创建光束处理器(核心)
      rayHandler = new RayHandler(world);
      // 6.创建一个点光源
      new PointLight(rayHandler, 100, Color.RED, 150f, Gdx.graphics.getWidth() / 2f, Gdx.graphics.getHeight() / 2f);
   }

   @Override
   public void resize(int width, int height) {
      camera.viewportWidth = width;
      camera.viewportHeight = height;
      camera.update();
   }

   @Override
   public void render() {
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      world.step(1/60f, 8, 3);
      rayHandler.setCombinedMatrix(camera);
      rayHandler.updateAndRender();
   }

   @Override
   public void pause() {

   }

   @Override
   public void resume() {

   }

   @Override
   public void dispose() {
      rayHandler.dispose();
   }
}

点光源的参数

  1. rayHandler:我们创建的RayHandler对象
  2. rays:光束个数,数量越大效果越逼真,但是性能消耗也越大,推荐最大不超过128
  3. color:灯光颜色
  4. distance 灯光的辐射距离,越大,灯光辐射的越远
  5. x:灯光在world中的横向中心点
  6. y:灯光在world中的纵向中心点

5.创建一个ConeLight

coneLight.png

public class ConeLightTest implements ApplicationListener {
    /** the camera **/
    OrthographicCamera camera;
    RayHandler rayHandler;
    World world;

    @Override
    public void create() {
        camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        camera.position.set(Gdx.graphics.getWidth() / 2f,Gdx.graphics.getHeight() / 2f, 0);
        camera.update();
        world = new World(new Vector2(0, -10), true);
        rayHandler = new RayHandler(world);
        new ConeLight(rayHandler, 100, Color.RED, 500f, Gdx.graphics.getWidth() / 2f, Gdx.graphics.getHeight() / 2f, 90, 60);
    }

    @Override
    public void resize(int width, int height) {
        camera.viewportWidth = width;
        camera.viewportHeight = height;
        camera.update();
    }

    @Override
    public void render() {
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        world.step(1 / 60f, 8, 3);
        rayHandler.setCombinedMatrix(camera);
        rayHandler.updateAndRender();
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {
        rayHandler.dispose();
    }
}

和点光源一样的配置,将AndroidLauncher的initialize参数改为ConeLightTest,ConeLight的参数和点光源差不多,多了2个参数

  1. directionDegree: 光源方向,逆时针为正方向 示例代码设置的90,0的话就是向右
  2. coneDegree: 锥形光的跨越角度 示例代码设置的60

6. 创建一个DirectionalLight

DirectionalLight.png

public class DirectionalLightTest implements ApplicationListener {
    /** the camera **/
    OrthographicCamera camera;
    RayHandler rayHandler;
    World world;
    protected Body body;
    protected Box2DDebugRenderer mWorldDebugger;
    private int viewportWidth = 0;
    private int viewportHeight = 0;

    @Override
    public void create() {
        viewportWidth = Gdx.graphics.getWidth();
        viewportHeight = Gdx.graphics.getHeight();
        camera = new OrthographicCamera(viewportWidth, viewportHeight);
        camera.position.set( viewportWidth/ 2f,viewportHeight/ 2f, 0);
        camera.update();
        world = new World(new Vector2(0, -10), true);
        mWorldDebugger = new Box2DDebugRenderer();
        makeBody();
        rayHandler = new RayHandler(world);
        new DirectionalLight(rayHandler, 100, Color.WHITE, 0);
    }

    private void makeBody() {
        CircleShape ballShape = new CircleShape();
        ballShape.setRadius(50f);

        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = ballShape;

        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyDef.BodyType.StaticBody;

        bodyDef.position.x = viewportWidth/ 2f;
        bodyDef.position.y = viewportHeight / 2f;
        body = world.createBody(bodyDef);
        body.createFixture(fixtureDef);
        ballShape.dispose();
    }

    @Override
    public void resize(int width, int height) {
        camera.viewportWidth = width;
        camera.viewportHeight = height;
        camera.update();
    }

    @Override
    public void render() {
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        world.step(1 / 60f, 8, 3);
        rayHandler.setCombinedMatrix(camera);
        rayHandler.updateAndRender();
        mWorldDebugger.render(world, camera.combined);
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {
        rayHandler.dispose();
    }
}

看到上面的效果图,可能很多疑问,这是什么鬼。
这个光源比较特殊,他就是无限长的光,参数也比较少

  1. rayHandler: 和之前一样,RayHandler对象
  2. rays:光束个数
  3. color:灯光颜色 示例设置的白色
    directionDegree:灯光照射角度,逆时针为正方向

这个光源设置后,如果画面上没有其他东西,就显示一个白屏,这就是这个光的特性,各处都照亮,所以一个屏幕都是白色,所以需要加上一个参照物,也就是示例代码和之前的多了一个body,光线照到body,然后被body遮挡就会显示阴影,也就成了上面示意图的模样,如果设置角度90,阴影就是朝上。

7.创建一个ChainLight

chain.png

public class ChainLightTest implements ApplicationListener {
    /** the camera **/
    OrthographicCamera camera;
    RayHandler rayHandler;
    World world;
    protected Body body;
    protected Box2DDebugRenderer mWorldDebugger;
    private int viewportWidth = 0;
    private int viewportHeight = 0;
    private ChainLight chainLight;
    private float bodyWidth = 50;
    private float bodyHeight = 50;
    @Override
    public void create() {
        RayHandler.setGammaCorrection(true);
        viewportWidth = Gdx.graphics.getWidth();
        viewportHeight = Gdx.graphics.getHeight();
        camera = new OrthographicCamera(viewportWidth, viewportHeight);
        camera.position.set( viewportWidth/ 2f,viewportHeight/ 2f, 0);
        camera.update();
        world = new World(new Vector2(0, -10), true);
        mWorldDebugger = new Box2DDebugRenderer();
        makeBody();
        rayHandler = new RayHandler(world);
        rayHandler.setAmbientLight(Color.GRAY);

        float[] vertices = new float[] {-bodyWidth / 2, -bodyHeight / 2,
                bodyWidth / 2, -bodyHeight / 2,
        bodyWidth / 2, bodyHeight / 2,
        -bodyWidth / 2, bodyHeight / 2,
                -bodyWidth / 2, -bodyHeight / 2};
        chainLight = new ChainLight(rayHandler, 100, Color.WHITE, 500, -1,
                vertices);
        chainLight.attachToBody(body, 0);
    }

    private void makeBody() {
        PolygonShape ballShape = new PolygonShape();
        ballShape.setAsBox(bodyWidth / 2, bodyHeight / 2);
        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = ballShape;

        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyDef.BodyType.StaticBody;

        bodyDef.position.x = viewportWidth/ 2f;
        bodyDef.position.y = viewportHeight / 2f;
        body = world.createBody(bodyDef);
        body.createFixture(fixtureDef);
        ballShape.dispose();
    }

    @Override
    public void resize(int width, int height) {
        camera.viewportWidth = width;
        camera.viewportHeight = height;
        camera.update();
    }

    @Override
    public void render() {
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        world.step(1 / 60f, 8, 3);
        rayHandler.setCombinedMatrix(camera);
        rayHandler.updateAndRender();
        mWorldDebugger.render(world, camera.combined);
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {
        rayHandler.dispose();
    }
}

ChainLight参数不一样的是

  1. rayDirection: 源码示例给的是1和-1 这个值其实代表发光角度,也可以设置其他数
  2. chain: 顶点数组
    ChainLight根据顶点确定发光体形状,rayDirection和chain的简单规律是
>> 1.1 如果是逆时针加入顶点 -1代表外部发光  1代表内部发光
>> 1.2 如果是顺时针加入顶点 -1代表内部发光  1代表外部发光

ChainLight比较特殊是必须依附于Body,Body决定了他的位置,示例是让一个正方形Body外围发光,顶点按照逆时针顺序添加。
唯一不接的一点是明明最后将原点设置为最后一个顶点,但是左下角却没有发光,不晓得是不是这个库的BUG

如果想定义圆形发光,比矩形麻烦点,需要很多个顶点,这些顶点连线形成这个圆形

RayHandler还有很多属性可以设置来加强模拟效果,可以多出尝试

注意:

  1. 如果没有调用RayHandler的serAmbinetLight设置环境光,而且开启了RayHandler模拟的话,界面是会一团黑的,你界面上绘制其他Actor是看不见的,除非有光照到他们
  2. 除了DirectionalLight,其他灯光都可以attachToBody

文章参考资料

  1. https://github.com/libgdx/box2dlights
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值