Opengl ES系列学习--点亮世界

     本节我们在上一节的基础上继续添加光照,我们要分析的目标就是《OpenGL ES应用开发实践指南 Android卷》书中第13章实现的最终的结果,代码下载请点击:Opengl ES Source Code,该Git库中的lighting Module就是我们本节要分析的目标,先看下本节最终实现的结果。

     在上一节的基础上,把天空盒换成了一个夜晚的天空盒,同时增加了光照,三个粒子发射器也增加了点光源,可以看到他们发射点的颜色非常亮。在开始分析代码之前,涉及到光照的几个知识点我们需要先搞清楚,否则后边分析代码时会非常困惑。

     1、用朗伯体反射实现方向光

     要实现漫反射,我们可以使用朗伯体反射。朗伯体反射描述了这样一个表面,它会反射所有方向上打到它的光线,以使它在任何观察点上看起来都是一样的,它的外观只依赖于它与光源所处的方位及其距离。我们在代码中要实现朗伯体反射,需要先计算出表面和光线之间夹角的余弦值,而要计算余弦值只需要计算指向光源的向量与表面法线的点积,它的工作原理是,当两个向量都是归一化的向量时,那两个向量的点积就是它们之间夹角的余弦值,这也正是朗伯体反射所需要的。来看一段代码实现,在当前lighting Module当中,它的高度图顶点着色器heightmap_vertex_shader.glsl中,定义了getDirectionalLighting方法,源码如下:

vec3 getDirectionalLighting()
{   
    return materialColor * 0.3 
         * max(dot(eyeSpaceNormal, u_VectorToLight), 0.0);       
}

     materialColor表示材质光,eyeSpaceNormal表示法线向量,u_VectorToLight表示方向光源,内建函数dot的作用是计算两个向量的点积,点积的结果也就是它们的余弦值,再和materialColor相乘得到的结果就是当前平面的朗伯体反射,乘以0.3只是为了调低光强度,结合这段代码,大家应该就可以明白我们如何使用朗伯体反射了。

     2、理解点光和方向光

     这点在我们Opengl ES系列学习--序小节的第五个问题点已经描述了,如果还有疑问,请回头搞清楚。

     3、使用点光源

     1)我们将位置和法线放入眼空间,在这个空间里,所有的位置和法线都是相对于照相机的位置和方位,这样做是为了使我们可以在同一个坐标空间中比较所有事物的距离和方位。我们为什么使用眼空间而不是世界空间呢?因为镜面光也依赖于照相机的位置,即使我们在本章中没有使用镜面光,学习如何使用眼空间也是一个好主意,这样我们在以后就可以直接使用它了。
     2)要把一个位置放进眼空间中,我们只需要让它与模型矩阵相乘,把它放入世界空间中;我们再把它与视图矩阵相乘,这样就把它入入眼空间中了。为了简化操作,我们可以把视力矩阵与模型矩阵相乘得到一个单一的矩阵,称为模型视图矩阵,再用这个矩阵把我们的位置放入眼空间中。
     3)如果模型视图矩阵只包含平稳和旋转,这对法线也是有用的,但是,如果我们缩放了一个物体,那会怎么样?如果缩放在所有方向上都是一样的,我们只需要重新归一化法线,使它的长度保持为1,但是如果某个方向上被压扁了,那我们必须要补偿那一方向。要达到这个目的,我们可以把倒置模型视图矩阵,然后再转置这个倒置的矩阵,让法线与该矩阵相乘,最后归一化结果。这块涉及到很多矩阵运算等高等数学的知识,自己也没搞清楚,暂时就知道这样使用就行了。

     第三点使用点光源的文字全部是从书上原本的抄过来的,从这里大家可以感觉到,要熟练或者精通使用Opengl ES,不光要懂得这些最基本的API,还必须懂得一些高等数学、物理学上的东西,我们才能明白,要实现一个结果,到底要用什么样的原理来实现,否则想不通这些,我们根本不知道代码该怎么写,我自己对这些原理性的东西也是一头雾水。

     讲了几个概念,接下来我们来看代码实现,还是和以前一样,关注点放在差异的部分,本节的目录结构如下:

     看一下本节的顶点着色器,代码比之前多了很多,后续我们要自己实现一些复杂的功能,都要往这个方向发展。控制更多的逻辑肯定是需要更多的代码了。我们逐个分析每个包下所有文件的不同。

     data包下完全相同,跳过;objects包下的Heightmap类和之前有差异,我们再次来分析一下,修改后的源码如下:

public class Heightmap {
    private static final int POSITION_COMPONENT_COUNT = 3;
    private static final int NORMAL_COMPONENT_COUNT = 3;
    private static final int TOTAL_COMPONENT_COUNT =
            POSITION_COMPONENT_COUNT + NORMAL_COMPONENT_COUNT;
    private static final int STRIDE =
            (POSITION_COMPONENT_COUNT + NORMAL_COMPONENT_COUNT) * BYTES_PER_FLOAT;

    private final int width;
    private final int height;
    private final int numElements;

    private final VertexBuffer vertexBuffer;
    private final IndexBuffer indexBuffer;

    public Heightmap(Bitmap bitmap) {
        width = bitmap.getWidth();
        height = bitmap.getHeight();
        if (width * height > 65536) {
            throw new RuntimeException("Heightmap is too large for the index buffer.");
        }
        numElements = calculateNumElements();
        vertexBuffer = new VertexBuffer(loadBitmapData(bitmap));
        indexBuffer = new IndexBuffer(createIndexData());
    }

    /**
     * Copy the heightmap data into a vertex buffer object.
     */
    private float[] loadBitmapData(Bitmap bitmap) {
        final int[] pixels = new int[width * height];
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
        bitmap.recycle();

        final float[] heightmapVertices =
                new float[width * height * TOTAL_COMPONENT_COUNT];

        int offset = 0;

        for (int row = 0; row < height; row++) {
            for (int col = 0; col < width; col++) {
                // The heightmap will lie flat on the XZ plane and centered
                // around (0, 0), with the bitmap width mapped to X and the
                // bitmap height mapped to Z, and Y representing the height. We
                // assume the heightmap is grayscale, and use the value of the
                // red color to determine the height.
                final Point point = getPoint(pixels, row, col);

                heightmapVertices[offset++] = point.x;
                heightmapVertices[offset++] = point.y;
                heightmapVertices[offset++] = point.z;

                final Point top = getPoint(pixels, row - 1, col);
                final Point left = getPoint(pixels, row, col - 1);
                final Point right = getPoint(pixels, row, col + 1);
                final Point bottom = getPoint(pixels, row + 1, col);

                final Vector rightToLeft = Geometry.vectorBetween(right, left);
                final Vector topToBottom = Geometry.vectorBetween(top, bottom);
                final Vector normal = rightToLeft.crossProduct(topToBottom).normalize();

                heightmapVertices[offset++] = normal.x;
                heightmapVertices[offset++] = normal.y;
                heightmapVertices[offset++] = normal.z;
            }
        }

        return heightmapVertices;
    }

    /**
     * Returns a point at the expected 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
opengles是一种用于在移动设备和嵌入式系统上实现2D和3D图形渲染的图形库。而gl-transitions是一个开源的OpenGL库,用于创建平滑过渡效果,可以应用于图像、视频等多种媒体内容。 要将gl-transitions移植到opengles上,需要进行以下步骤: 1. 确定opengles版本:gl-transitions可能使用的是OpenGL的较新版本,而opengles可能只支持较旧的版本。因此,首先需要确定opengles版本,并了解其与OpenGL之间的差异。 2. 了解gl-transitions的实现:深入了解gl-transitions的实现方式和代码结构,理解其对OpenGL的使用方式和功能。 3. 理解opengles的API:熟悉opengles的API,包括顶点/片段着色器、缓冲区对象、纹理对象等。理解opengles的渲染管线和数据传递方式,以便能够正确地将gl-transitions移植到opengles上。 4. 逐步移植:根据gl-transitions的实现和opengles的API,逐步将gl-transitions的代码移植为opengles可用的代码。这可能涉及到对着色器代码的修改、纹理对象的创建和绑定、缓冲区对象的使用等。 5. 调试和测试:移植完成后,进行调试和测试以确保移植后的代码在opengles上正常工作,并且能够正确地渲染出所需的过渡效果。 需要注意的是,由于较新版本的OpenGL可能具有一些opengles不支持的功能,因此在移植过程中可能需要做一些功能的调整或替代。此外,移植过程中可能还需要考虑设备的性能和兼容性问题,确保移植后的代码能够在目标设备上流畅地运行。 总结起来,将gl-transitions移植到opengles上需要对opengles的API有很好的了解,并根据它的渲染方式和数据传递方式对gl-transitions的代码进行适当的修改和调整。这样才能确保移植后的代码能够在opengles上正常运行并呈现出所需的过渡效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红-旺永福

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值