精华内容
下载资源
问答
  • 我想在一个坐标轴上显示很多坐标,我一般用glRasterPos2f(0,-0.1); //起始位置 drawString("1");来显示文字,但是坐标要标注的太多了,我也不能一个一个的写出位置来啊,有什么简单的方法显示吗?就像c语言中...
  • OpenGL显示文字

    千次阅读 2019-06-23 19:33:06
    阅读这篇文章前,可以先看一下这个文字显示系列的...OpenGL提供的是图形API,本身并不提供文字处理方面的接口,如果想要显示文字就需要先将文字转化成图片,然后渲染图片。 Char Map Char Map是最简单的方式,首先...

    阅读这篇文章前,可以先看一下这个文字显示系列的其他文章,了解一些字符编码,字体相关的知识:https://blog.csdn.net/wlk1229/article/category/9008450

     

    显示文字

    OpenGL提供的是图形API,本身并不提供文字处理方面的接口,如果想要显示文字就需要先将文字转化成图片,然后渲染图片。

    Char Map

    Char Map是最简单的方式,首先需要准备号一张图片,图片中包含了要显示的文字。显示文字时只需要从图片中截取相应的纹理就可以了。这种方式比较适合文字大小固定,显示字符比较少的情况,例如显示数字,只需要“0123456789”十个字符图片就行,有时还会需要“.,”。而且所有数字的宽度一般都是一样的,这对于截取纹理来说非常容易。如下就是一张数字图片:

     

    有时我们不光需要数字,也会需要其他字符,可以使用Bitmap font软件导出相应的文字图片,软件界面如下图:

     

    软件可以导出文字使用的字体,文字的大小,加粗,斜体等等。也可设置导出图片的格式,文字之间的间隔等等,如下两张图片。

       

    在设置导出图片时,有一个导出文字的描述文件“Font descriptor”的选项,这是因为为了提高导出的图片的利用率,文字会紧密排列在图片中,为了知道每个文字在图片中的位置就需要,一个单独的文件来描述。如下,是导出的图片,和相应的描述文件:

    描述文件是一个“*.fnt”后缀名的文件,可以直接用记事本打开,从这个文件中可以知道文字的行高“lineHeigt”,字符的Unicode编码“id”,字符宽度“xadvance”,以及字符纹理在导出图片中的位置和长宽等等。有了这些信息,加上导出的图片就可以显示文字了。

     

    FreeType解析字体

    虽然Char Map的方式可以很好的显示文字,但有一定局限,1.显示的文字有限,对于英文来说字符比较少,可以用一张图片把所有字符打包,但是对于中文,要把所有字符全部打包就不是那么容易了,一般情况下只打包用到的字符;2.字体是固定的,文字的大小也是固定的,如果想更换字体则需要重新打包生成图片。Char Map本身只适合一些固定文字的显示,如果文字内容会变化,或者需要改变字体则不适合。

     

    显示文字完全可以通过解析字体文件,然后获取字体的图片,完成文字显示。只要我们有字体文件,字体文件中包含了我们要显示的文字(一般中文字体会包含所有要显示的字符),可以通过FreeType解析字体文件,获取相应的字符图片。通过FreeType解析字体,可以随意更换要显示文字的字体,也可以改变文字的大小等等。

     

    通过FreeType这个库可以很容易的从字体文件中获取到字符的位图。FreeType是一个C语言的库,支持各个平台,其提供了CMake编译,可以通过CMake软件转换成VS的工程,这里我已经编译好了VS2015中FreeType静态库的DebugRelease版本。

     

    FreeType使用也比较简单,这里给一个字符的大致步骤,详细的可以参考官方文档。

    1. 调用FT_Init_FreeType初始化FreeType库
    2. 调用FT_New_Face使用字体文件创建字体
    3. 调用FT_Select_Charmap设置映射字符编码,默认字符编码是Unicode,通常不用设置,因为大部分字体只支持Unicode和一种Apple平台的老编码。
    4. 调用FT_Set_Char_Size设置字符大小,设置字体大小时要设置字号和DPI,字号和平时用Word中字号类似,DPI是指显示设备像素密度,一般电脑显示器DPI在100左右,而手机在300左右,两者共同决定导出位图的大小。
    5. 调用FT_Set_Transform设置字体的缩放旋转和排版位置。如果想自己控制排版位置,一般可以省略这个步骤。
    6. 调用FT_Load_Char获取文字的位图信息,默认获取的是8位色深的位图,即每个字节代表一个像素。
    7. 使用位图绘制就可以显示文字了。

     

    字体缓存

    通常显示文字的时候都会有好多字符,如果每次显示文字的时候去获取一次位图,然后创建纹理,这样的效率太低。显示文字的时候会有很多重复的字符,同一个字符纹理只需要获取一次就行;另外如果每个字符创建一个纹理,这会导致创建好多纹理,所以需要一张大的纹理把需要显示的文字打包起来。因为所有显示的文字都在一个纹理中,显示文字的时候可以通过一次调用glDrawArrays渲染一个字符串,大大提高渲染效率。

    缓存字体时需要注意以下几个问题

    1. 因为像素坐标是整数,OpenGL中纹理坐标是浮点数,为了防止像素挨得太近,导致显示字符的时候出现其他字符的像素,字符之间需要留一定的空隙。
    2. OpenGL在设置纹理数据时默认是4字节对齐,而FreeType获取的是8bit位图,每个像素只有1个字节,所以需要将纹理数据对其设为一个字节,需要调用glPixelStorei(GL_UNPACK_ALIGNMENT, 1)函数。
    3. 更新字体纹理缓存中的字符是可以通过函数glTexSubImage2D只更新一个字符的数据。
    4. OpenGL中纹理坐标是以坐下角为原点,Y轴是从下到上,而FreeType获取的位图图原点是左上角,Y轴是从上到下,与OpenGL相反,所以纹理缓存中的纹理实际如下:

     

    文字边框

    FreeType获取的位图是一张刚好包只含文字的位图,不包含左右上下的空白信息。如果绘制文字时直接把每一张位图连接在一起,文字则会一个粘一个,不利于阅读,正常显示的文字上下左右都会有一定的间距。

    如上图外面的大矩形框是显示中字时需要的位置,内部红色框是FreeType获取的位图。为了正确显示文字,需要六个位置信息,图中的Height、Width、OffsetX、OffsetY已经位图的长宽。

    这六个信息可以通过以下方式获得:

    1. Height,当调用完FT_Set_Char_Size后,所有字符的高度都是一样的,在FT_Set_Char_Size设置文字大小后,可以通过fontFace->size->metrics.height/64获得,除以64说因为FreeType获取的字体高度单位的原因。
    2. Width,当调用完FT_Load_Char后,可以通过fontFace->glyph->advance.x/64,也需要除以64。
    3. OffsetX,当调用完FT_Load_Char后,为fontFace->glyph->metrics.horiBearingX/64。
    4. OffsetY,当调用完FT_Load_Char后,为(fontFace->size->metrics.height + fontFace->size->metrics.descender - fontFace->glyph->metrics.horiBearingY)/64。
    5. Bitmap宽,当调用完FT_Load_Char后,为fontFace->glyph->bitmap.width。
    6. Bitmap高,当调用完FT_Load_Char后,为fontFace->glyph->bitmap.rows。

    以上数据主要根据FreeType文档中的这张图片获得:

    其他请参考,FreeType文档

     

    除了以上信息,对于每个字符,渲染文字时还需要保存字符纹理在字体缓存纹理中的位置,所以总共需要8个位置信息,我使用了以下结构体存储:
     

    struct CharGlyphRect
    {
    	int width, height; //字符的宽高
    	int offsetX, offsetY; // 字符纹理在字符矩形中的偏移量, 坐标系为X从左到右,Y从上到下
    	int texWidth, texHeight; // 字符纹理的宽高
    	int texX, texY; //字符在缓存大纹理中的位置, 坐标系为X从左到右,Y从上到下
    	CharGlyphRect() :texWidth(0), texHeight(0), width(0), height(0), offsetX(0), offsetY(0), texX(0), texY(0) {}
    };

    为了保存所有已缓存的字符,需要使用一个map,map<wchar_t, CharGlyphRect> CharCache。

     

    渲染文字

    渲染文字时我们需要渲染一个矩形,如下图时渲染的文字与其对应的矩形:

    渲染的矩形并不是紧挨着的,以为我们的文字纹理只是刚好包含的字符的纹理,没有包括空隙,这个空隙需要我们自己控制。为了一次渲染多个文字,而文字渲染的矩形又不是紧挨着,所以不能使用GL_TRIANGLE_STRIP,只能使用GL_TRIANGLES。一个矩形需要两个三角形,所以需要六个点,六个点的坐标设置如下:
     

    	const auto& glyphRect = CharCache[str[i]];
    
    	int x = currentX + glyphRect.offsetX;
    	int y = currentY - glyphRect.offsetY;
    
    	int texX = glyphRect.texX;
    	int texY = glyphRect.texY;
    		 
    	//以下坐标是渲染时的NDC坐标
    	//左上角
    	verts[i * 6].pos[0] = PerPixelWidth * x;
    	verts[i * 6].pos[1] = PerPixelHeight * y;
    	verts[i * 6].texPos[0] = texX / static_cast<float>(CharCacheWidth);
    	verts[i * 6].texPos[1] = texY/ static_cast<float>(CharCacheHeight);
    
    	//左下角
    	verts[i * 6 + 1].pos[0] = PerPixelWidth * x;
    	verts[i * 6 + 1].pos[1] = PerPixelHeight * (y - glyphRect.texHeight);
    	verts[i * 6 + 1].texPos[0] = texX / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 1].texPos[1] = (texY + glyphRect.texHeight) / static_cast<float>(CharCacheHeight);
    
    	//右上角
    	verts[i * 6 + 2].pos[0] = PerPixelWidth * (x + glyphRect.texWidth);
    	verts[i * 6 + 2].pos[1] = PerPixelHeight * y;
    	verts[i * 6 + 2].texPos[0] = (texX + glyphRect.texWidth) / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 2].texPos[1] = texY / static_cast<float>(CharCacheHeight);
    
    	//右上角
    	verts[i * 6 + 3].pos[0] = PerPixelWidth * (x + glyphRect.texWidth);
    	verts[i * 6 + 3].pos[1] = PerPixelHeight * y;
    	verts[i * 6 + 3].texPos[0] = (texX + glyphRect.texWidth) / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 3].texPos[1] = texY  / static_cast<float>(CharCacheHeight);
    
    	//左下角
    	verts[i * 6 + 4].pos[0] = PerPixelWidth * x;
    	verts[i * 6 + 4].pos[1] = PerPixelHeight * (y - glyphRect.texHeight);
    	verts[i * 6 + 4].texPos[0] = texX / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 4].texPos[1] = (texY + glyphRect.texHeight) / static_cast<float>(CharCacheHeight);
    
    	//右下角
    	verts[i * 6 + 5].pos[0] = PerPixelWidth * (x + glyphRect.texWidth);
    	verts[i * 6 + 5].pos[1] = PerPixelHeight * (y - glyphRect.texHeight);
    	verts[i * 6 + 5].texPos[0] = (texX + glyphRect.texWidth) / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 5].texPos[1] = (texY + glyphRect.texHeight) / static_cast<float>(CharCacheHeight);
    
    	currentX += glyphRect.width;
    

    平滑字体

    文字显示中有大量的曲线,如果要显示清晰的文字必须要让曲线看上去非常平滑,不能出现锯齿。

    FreeType默认获取的位图是一个8bit的位图,每个像素只有一个字节,这是一种256度灰阶图。正是这个8位的位图可以让显示的字符具有反走样的能力,可以显示平滑清晰的文字。在OpenGL中最常用的一种抗锯齿反走样方式就是利用透明来进行反走样,具体原理参考。因为FreeType提供的是灰阶图,所以可以设置一个文字颜色,然后把这个灰阶设为像素的透明度,这样显示的文字可以非常平滑,没有锯齿。

    片元着色器如下:

    #version 430

    layout(location= 2) uniform sampler2D tex; //缓存字体纹理

    layout(location= 3) uniform vec3 color; //文字颜色

     

    out vec4 outColor;

    in vec2 texCoord;

    void main()

    {

        float a = texture(tex, texCoord).r;

        outColor = vec4(color, a);

    }

     

    在绘制的时候,因为要使用透明,所以需要开启融合,代码如下:

    glActiveTexture(GL_TEXTURE0);

    glBindTexture(GL_TEXTURE_2DCharCacheTex);

    glUniform1i(2, 0);

    glUniform3fv(3, 1, _color); //设置文字颜色

     

    glEnable(GL_BLEND);

    glBlendFunc(GL_SRC_ALPHAGL_ONE_MINUS_SRC_ALPHA);//设置融合函数

     

    glBindVertexArray(_vao);

    glDrawArrays(GL_TRIANGLES, 0, _vertCount);

    glDisable(GL_BLEND);

     

    以下是一个完整的例子,VS2017代码下载地址

     

    展开全文
  • OpenGL 输出中文 英文 数字

    千次阅读 2014-11-20 18:18:23
    OpenGL 输出中文 英文 数字 将文字以轮廓与点形式表现

    OpenGL 输出中文 英文 数字


    将文字以轮廓与点形式表现







    展开全文
  • 本文讲述用OpenGL显示STL模型文件。     STL模型文件:Android OpenGL STL详解   Android OpenGL初级开发:Android OpenGL之使用GLSurfaceView创建一个3D旋转的图形     1.Model.java public class ...

    本文讲述用OpenGL显示STL模型文件。

     

     

    STL模型文件:Android OpenGL STL详解

     

    Android OpenGL初级开发:Android OpenGL之使用GLSurfaceView创建一个3D旋转的图形

     

     

    1.Model.java

    public class Model {
        //三角面个数
        private int facetCount;
        //顶点坐标数组
        private float[] verts;
        //每个顶点对应的法向量数组
        private float[] vnorms;
        //每个三角面的属性信息
        private short[] remarks;


        //顶点数组转换而来的Buffer
        private FloatBuffer vertBuffer;


        //每个顶点对应的法向量转换而来的Buffer
        private FloatBuffer vnormBuffer;
        //以下分别保存所有点在x,y,z方向上的最大值、最小值
        float maxX;
        float minX;
        float maxY;
        float minY;
        float maxZ;
        float minZ;


        //返回模型的中心点
        public Point getCentrePoint() {
            float cx = (maxX - minX) / 2;
            float cy = (maxY - minY) / 2;
            float cz = (maxZ - minZ) / 2;
            return new Point(cx, cy, cz);
        }


        //包裹模型的最大半径
        public float getR() {
            float dx = (maxX - minX);
            float dy = (maxY - minY);
            float dz = (maxZ - minZ);
            float max = dx;
            if (dy > max)
                max = dy;
            if (dz > max)
                max = dz;
            return max;
        }


        public int getFacetCount() {
            return facetCount;
        }


        public void setFacetCount(int facetCount) {
            this.facetCount = facetCount;
        }


        public float[] getVerts() {
            return verts;
        }


        public void setVerts(float[] verts) {
            this.verts = verts;
            vertBuffer = Util.floatToBuffer(verts);
        }


        public FloatBuffer getVertBuffer() {


            return vertBuffer;
        }


        public FloatBuffer getVnormBuffer() {
            return vnormBuffer;
        }


        public float[] getVnorms() {
            return vnorms;
        }


        public void setVnorms(float[] vnorms) {
            this.vnorms = vnorms;
            vnormBuffer = Util.floatToBuffer(vnorms);
        }


        public short[] getRemarks() {
            return remarks;
        }


        public void setRemarks(short[] remarks) {
            this.remarks = remarks;
        }




     

    }

     

    2.Point.java

    public class Point {
        public float x;
        public float y;
        public float z;


        public Point(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;


        }

    }

     

    3.STLReader.java

    public class STLReader {
        private StlLoadListener stlLoadListener;


        public Model parserBinStlInSDCard(String path)
                throws IOException {


            File file = new File(path);
            FileInputStream fis = new FileInputStream(file);
            return parserBinStl(fis);
        }


        public Model parserBinStlInAssets(Context context, String fileName)
                throws IOException {


            InputStream is = context.getAssets().open(fileName);
            return parserBinStl(is);
        }
        

        

        public static interface StlLoadListener {
            void onstart();


            void onLoading(int cur, int total);


            void onFinished();


            void onFailure(Exception e);
        }
    }

     

     

    4.OpenGLESActivity.java

    public class OpenGLESActivity extends Activity {


        private GLSurfaceView glView;
        private float rotateDegreen = 0;
        private GLRenderer glRenderer;


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 如果本设备支持OpenGl ES 2.0
            if (IsSupported()) {
                glView = new GLSurfaceView(this);
                glRenderer = new GLRenderer(this);
                glView.setRenderer(glRenderer);
                setContentView(glView);
            }else{
                Toast.makeText(OpenGLESActivity.this,"此设备不支持OpenGL ES 2.0!",Toast.LENGTH_LONG).show();
            }
        }


        public void rotate(float degree) {
            glRenderer.rotate(degree);
            glView.invalidate();
        }


        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                rotate(rotateDegreen);
            }
        };




        /**
         * 当前设备是否支持OpenGL ES 2.0
         * */


        private boolean IsSupported() {
            ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
            ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
            boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000
                    ||(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && (Build.FINGERPRINT.startsWith("generic")
                    || Build.FINGERPRINT.startsWith("unknown")
                    || Build.MODEL.contains("google_sdk")
                    || Build.MODEL.contains("Emulator")
                    || Build.MODEL.contains("Android SDK built for x86")));
            return supportsEs2;
        }


        /**
         * onPause方法 glSurfaceView.onPause
         * */


        @Override
        protected void onPause() {
            super.onPause();
            if (glView != null) {
                glView.onPause();
            }
        }


        /**
         * onResume方法 glSurfaceView.onResume
         * */


        @Override
        protected void onResume() {
            super.onResume();
            if (glView != null) {
                glView.onResume();


                //不断改变rotateDegreen值,实现旋转
                new Thread() {
                    @Override
                    public void run() {
                        while (true) {
                            try {
                                sleep(100);


                                rotateDegreen += 5;
                                handler.sendEmptyMessage(0x001);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }


                        }
                    }
                }.start();
            }
        }


    }

     

     

    5.结果

     

    Demo下载:https://download.csdn.net/download/weixin_37730482/10449292

    展开全文
  • OpenGL显示DEM.zip

    2020-05-30 18:16:15
    使用C++和OpenGL设计了一个程序,读取txt格式的数字高程模型,三维显示。用鼠标和键盘控制漫游。
  • 如何 OPenGL 显示数字

    2013-01-15 10:20:30
    例如像0.03这种小数要显示在窗口中,求解答。
  • 在上一篇文章中,我们知道了如何在Android开发一个OpenGL模型显示。但是并没有对具体模型数据进行显示,只是展示一个背景颜色而已,在本章中,我们学习如何将一个模型数据显示成一个具体的3D图形。在Android中开发...

    在上一篇文章中,我们知道了如何在Android开发一个OpenGL模型显示。但是并没有对具体模型数据进行显示,只是展示一个背景颜色而已,在本章中,我们学习如何将一个模型数据显示成一个具体的3D图形。在Android中开发OpenGL程序非常简单,但是却有很多OpenGL相关概念是必须要清楚的,了解这些相关概念才能写出正确的代码,否则,你写出来的程序可能会无缘无故崩溃,或者是画出来的模型显示不出来等等问题。

    本文是建立在上一篇文章之上,只修改GLRender类,其他部分保持不变。

    1 模型数据

    前面我们说过,一个3D模型一般是由很多三角片(或四边形)组成,因此,首先我们需要有三角形的点数据。既然是3D模型,自然每个点坐标是在三维坐标系中,因此,每个点需要3个数来表示。

    我们定义一个三角形,需要9个数,如果我们有float类型表示一个数,那么定义一个三角形(三个点)如下:

     private float[] mTriangleArray = {
                0f, 1f, 0f,
                -1f, -1f, 0f,
                1f, -1f, 0f
    };

    此时,我们就有了一个三角形的3个点数据了。但是,OpenGL并不是对堆里面的数据进行操作,而是在直接内存中(Direct Memory),即操作的数据需要保存到NIO里面的Buffer对象中。而我们上面声明的float[]对象保存在堆中,因此,需要我们将float[]对象转为java.nio.Buffer对象。

    我们可以选择在构造函数里面,将float[]对象转为java.nio.Buffer,如下所示:

    private FloatBuffer mTriangleBuffer;
    public GLRenderer() {
        //先初始化buffer,数组的长度*4,因为一个float占4个字节
        ByteBuffer bb = ByteBuffer.allocateDirect(mTriangleArray.length * 4);
        //以本机字节顺序来修改此缓冲区的字节顺序
        bb.order(ByteOrder.nativeOrder());
        mTriangleBuffer = bb.asFloatBuffer();
        //将给定float[]数据从当前位置开始,依次写入此缓冲区
        mTriangleBuffer.put(mTriangleArray);
        //设置此缓冲区的位置。如果标记已定义并且大于新的位置,则要丢弃该标记。 
        mTriangleBuffer.position(0);
    
    }

    注意,ByteBufferFloatBuffer以及IntBuffer都是继承自抽象类java.nio.Buffer

    另外,OpenGL在底层的实现是C语言,与Java默认的数据存储字节顺序可能不同,即大端小端问题。因此,为了保险起见,在将数据传递给OpenGL之前,我们需要指明使用本机的存储顺序。

    此时,我们顺利地将float[]转为了FloatBuffer,后面绘制三角形的时候,直接通过成员变量mTriangleBuffer即可。

    2 矩阵变换

    在现实世界中,我们要观察一个物体可以通过如下几种方式:

    • 从不同位置去观察。(视图变换)
    • 移动或旋转物体,放缩物体(虽然实际生活中不能放缩,但是计算机世界是可以的)。(模型变换)
    • 给物体拍照印成照片。可以做到“近大远小”、裁剪只看部分等等透视效果。(投影变换)
    • 只拍摄物体的一部分,使得物体在照片中只显示部分。(视口变换)

    上面所述效果,可以在OpenGL中全部实现。有一点需要很清楚,就是OpenGL的变换其实都是通过矩阵相乘来实现的。

    2.1 模型变换和视图变换

    高中我们学过相对运动,就是说,改变观测点的位置与改变物体位置都可以达到等效的运动效果。因此,在OpenGL中,这两种变换本质上用的是同一个函数。

    在进行变换之前,我们需要声明当前是使用哪种变换。在本节中,声明使用模型视图变换,而模型视图变换在OpenGL中对应标识为:GL10.GL_MODELVIEW。通过glMatrixMode函数来声明:

    gl.glMatrixMode(GL10.GL_MODELVIEW);

    接下来你就可以对模型进行:平移、放缩、旋转等操作啦。但是有一点值得注意的是,在此之前,你可能针对模型做了其他的操作,而我们知道,每次操作相当于一次矩阵相乘。OpenGL中,使用“当前矩阵”表示要执行的变化,为了防止前面执行过变换“保留”在“当前矩阵”,我们需要把“当前矩阵”复位,即变为单位矩阵(对角线上的元素全为1),通过执行如下函数:

    gl.glLoadIdentity();

    此时,当前变换矩阵为单位矩阵,后面才可以继续做变换,例如:

    
    //绕(1,0,0)向量旋转30度
    gl.glRotatef(30, 1, 0, 0);
    
    //沿x轴方向移动1个单位
    gl.glTranslatef(1, 0, 0);
    
    //x,y,z方向放缩0.1倍
    gl.glScalef(0.1f, 0.1f, 0.1f);

    上面的效果都是矩阵相乘实现,因此我们需要注意变换次序问题,举个例子,假设“当前矩阵”为单位矩阵,然后乘以一个表示旋转的矩阵R,再乘以一个表示移动的矩阵T,最后得到的矩阵,再与每个顶点相乘。假设表示模型所以顶点的矩阵为V,则实际就是((RT)V),由矩阵乘法结合律,((RT)V)=(R(TV)),这导致的就是,先移动再旋转。即:

    实际变换顺序与代码中的顺序是相反的

    上面所讲的都是改变物体的位置或方向来实现“相对运动”的,如果我们不想改变物体,而是改变观察点,可以使用如下函数

    /**
    * gl: GL10型变量
    * eyeX,eyeY,eyeZ: 观测点坐标(相机坐标)
    * centerX,centerY,centerZ:观察位置的坐标
    * upX,upY,upZ :相机向上方向在世界坐标系中的方向(即保证看到的物体跟期望的不会颠倒)
    */
    GLU.gluLookAt(gl,eyeX,eyeY,eyeZ,centerX,centerY,centerZ,upX,upY,upZ);

    2.2 投影变换

    投影变换就是定义一个可视空间,可视空间之外的物体是看不到的(即不会再屏幕中)。在此之前,我们的三维坐标中的三个坐标轴取值为[-1,1],从现在开始,坐标可以不再是从-11了!

    OpenGL支持主要两种投影变换:

    • 透视投影
    • 正投影

    当然了,投影也是通过矩阵来实现的,如果想要设置为投影变换,跟前面类似:

    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();

    同样的道理,glLoadIdentity()函数也需要立即调用。

    通过如下函数可将当前可视空间设置为透视投影空间:

    gl.glFrustumf(left,right,bottom,top,near,far);

    上面函数对应参数如下图所示(图片出自www.opengl.org):

    透视投影变换glFrustumf

    当然了,也可以通过另一个函数实现相同的效果:

     GLU.gluPerspective(gl,fovy,aspect,near,far);

    上面函数对应的参数如下图所示(图片出自www.opengl.org):

    透视投影变换gluPerspective

    而对于正投影来说,相当于观察点处于无穷远,当然了,这是一种理想状态,但是有时使用正投影效率可能会更高。可以通过如下函数设置正投影:

    gl.glOrthof(left,right,bottom,top,near,far);

    上面函数对应的参数如下图所示(图片出自www.opengl.org): 
    正投影

    2.3 视口变换

    我们可以选择将图像绘制到屏幕窗口的那个区域,一般默认是在整个窗口中绘制,但是,如果你不希望在整个窗口中绘制,而是在窗口的某个小区域中绘制,你也可以自己定制:

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);
    }

    每次窗口发生变化时,我们可以设置绘制区域,即在onSurfaceChanged函数中调用glViewport函数。

    3 启用相关功能及配置

    3.1 glClearColor()

    设置清屏颜色,每次清屏时,使用该颜色填充整个屏幕。使用例子:

     gl.glClearColor(1.0f, 1.0f, 1.0f, 0f);

    里面参数分别代表RGBA,取值范围为[0,1]而不是[0,255]

    3.2 glDepthFunc()

    OpenGL中物体模型的每个像素都有一个深度缓存的值(在01之间,可以看成是距离),可以通过glClearDepthf函数设置默认的“当前像素”z值。在绘制时,通过将待绘制的模型像素点的深度值与“当前像素”z值进行比较,将符合条件的像素绘制出来,不符合条件的不绘制。具体的“指定条件”可取以下值:

    • GL10.GL_NEVER:永不绘制
    • GL10.GL_LESS:只绘制模型中像素点的z<当前像素z值的部分
    • GL10.GL_EQUAL:只绘制模型中像素点的z=当前像素z值的部分
    • GL10.GL_LEQUAL:只绘制模型中像素点的z<=当前像素z值的部分
    • GL10.GL_GREATER :只绘制模型中像素点的z>当前像素z值的部分
    • GL10.GL_NOTEQUAL:只绘制模型中像素点的z!=当前像素z值的部分
    • GL10.GL_GEQUAL:只绘制模型中像素点的z>=当前像素z值的部分
    • GL10.GL_ALWAYS:总是绘制

    通过目标像素与当前像素在z方向上值大小的比较是否满足参数指定的条件,来决定在深度(z方向)上是否绘制该目标像素。

    注意, 该函数只有启用“深度测试”时才有效,通过glEnable(GL_DEPTH_TEST)开启深度测试以及glDisable(GL_DEPTH_TEST)关闭深度测试。

    例子:

     gl.glDepthFunc(GL10.GL_LEQUAL);

    3.3 glClearDepthf()

    给深度缓存设定默认值。

    缓存中的每个像素的深度值默认都是这个, 假设在 gl.glDepthFunc(GL10.GL_LEQUAL);前提下:

    • 如果指定“当前像素值”为1时,我们知道,一个模型深度值取值和范围为[0,1]。这个时候你往里面画一个物体, 由于物体的每个像素的深度值都小于等于1, 所以整个物体都被显示了出来。
    • 如果指定“当前像素值”为0, 物体的每个像素的深度值都大于等于0, 所以整个物体都不可见。 
      如果指定“当前像素值”为0.5, 那么物体就只有深度小于等于0.5的那部分才是可见的

    使用例子:

    gl.glClearDepthf(1.0f);

    3.3 glEnable(),glDisable()

    glEnable()启用相关功能,glDisable()关闭相关功能。

    比如:

    //启用深度测试
    gl.glEnable(GL10.GL_DEPTH_TEST);
    //关闭深度测试
    gl.glDisable(GL10.GL_DEPTH_TEST)
    //开启灯照效果
    gl.glEnable(GL10.GL_LIGHTING);
    // 启用光源0
    gl.glEnable(GL10.GL_LIGHT0);
    // 启用颜色追踪
    gl.glEnable(GL10.GL_COLOR_MATERIAL);

    3.5 glHint()

    如果OpenGL在某些地方不能有效执行是,给他指定其他操作。

    函数原型为:

    void glHint(GLenum target,GLenum mod)

    其中,target:指定所控制行为的符号常量,可以是以下值(引自【OpenGL函数思考-glHint 】):

    • GL_FOG_HINT:指定雾化计算的精度。如果OpenGL实现不能有效的支持每个像素的雾化计算,则GL_DONT_CAREGL_FASTEST雾化效果中每个定点的计算。
    • GL_LINE_SMOOTH_HINT:指定反走样线段的采样质量。如果应用较大的滤波函数,GL_NICEST在光栅化期间可以生成更多的像素段。
    • GL_PERSPECTIVE_CORRECTION_HINT:指定颜色和纹理坐标的差值质量。如果OpenGL不能有效的支持透视修正参数差值,那么GL_DONT_CARE 和 GL_FASTEST可以执行颜色、纹理坐标的简单线性差值计算。
    • GL_POINT_SMOOTH_HINT:指定反走样点的采样质量,如果应用较大的滤波函数,GL_NICEST在光栅化期间可以生成更多的像素段。
    • GL_POLYGON_SMOOTH_HINT:指定反走样多边形的采样质量,如果应用较大的滤波函数,GL_NICEST在光栅化期间可以生成更多的像素段。

    mod:指定所采取行为的符号常量,可以是以下值:

    • GL_FASTEST:选择速度最快选项。
    • GL_NICEST:选择最高质量选项。
    • GL_DONT_CARE:对选项不做考虑。

    例子:

    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);

    3.6 glEnableClientState()

    当我们需要启用顶点数组(保存每个顶点的坐标数据)、顶点颜色数组(保存每个顶点的颜色)等等,就要通过glEnableClientState()函数来开启:

    //以下两步为绘制颜色与顶点前必做操作
    // 允许设置顶点
    //GL10.GL_VERTEX_ARRAY顶点数组
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    // 允许设置颜色
    //GL10.GL_COLOR_ARRAY颜色数组
    gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

    3.7 glShadeModel()

    设置着色器模式,有如下两个选择:

    • GL10.GL_FLAT
    • GL10.GL_SMOOTH(默认)

    如果为每个顶点指定了顶点的颜色,此时:

    • GL_SMOOTH:根据顶点的不同颜色,最终以渐变的形式填充图形。
    • GL_FLAT:假设有n个三角片,则取最后n个顶点的颜色填充着n个三角片。

    使用例子:

    gl.glShadeModel(GL10.GL_SMOOTH);

     

    4 开始绘制

    前面讲了很多概念,但是其实都是非常值得学习的。有了这些基础,我们才能理解如何写OpenGL,从上一篇文章中我们知道,开发OpenGL大部分工作都是在Renderer类上面,我直接粘Renderder代码:

    public class GLRenderer implements GLSurfaceView.Renderer {
        private float[] mTriangleArray = {
                0f, 1f, 0f,
                -1f, -1f, 0f,
                1f, -1f, 0f
        };
        //三角形各顶点颜色(三个顶点)
        private float[] mColor = new float[]{
                1, 1, 0, 1,
                0, 1, 1, 1,
                1, 0, 1, 1
        };
        private FloatBuffer mTriangleBuffer;
        private FloatBuffer mColorBuffer;
    
    
        public GLRenderer() {
            //点相关
            //先初始化buffer,数组的长度*4,因为一个float占4个字节
            ByteBuffer bb = ByteBuffer.allocateDirect(mTriangleArray.length * 4);
            //以本机字节顺序来修改此缓冲区的字节顺序
            bb.order(ByteOrder.nativeOrder());
            mTriangleBuffer = bb.asFloatBuffer();
            //将给定float[]数据从当前位置开始,依次写入此缓冲区
            mTriangleBuffer.put(mTriangleArray);
            //设置此缓冲区的位置。如果标记已定义并且大于新的位置,则要丢弃该标记。
            mTriangleBuffer.position(0);
    
    
            //颜色相关
            ByteBuffer bb2 = ByteBuffer.allocateDirect(mColor.length * 4);
            bb2.order(ByteOrder.nativeOrder());
            mColorBuffer = bb2.asFloatBuffer();
            mColorBuffer.put(mColor);
            mColorBuffer.position(0);
        }
    
        @Override
        public void onDrawFrame(GL10 gl) {
    
    
            // 清除屏幕和深度缓存
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            // 重置当前的模型观察矩阵
            gl.glLoadIdentity();
    
            // 允许设置顶点
            //GL10.GL_VERTEX_ARRAY顶点数组
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            // 允许设置颜色
            //GL10.GL_COLOR_ARRAY颜色数组
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
    
            //将三角形在z轴上移动
            gl.glTranslatef(0f, 0.0f, -2.0f);
    
            // 设置三角形
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mTriangleBuffer);
            // 设置三角形颜色
            gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
            // 绘制三角形
            gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
    
    
            // 取消颜色设置
            gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
            // 取消顶点设置
            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    
            //绘制结束
            gl.glFinish();
    
        }
    
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            float ratio = (float) width / height;
            // 设置OpenGL场景的大小,(0,0)表示窗口内部视口的左下角,(w,h)指定了视口的大小
            gl.glViewport(0, 0, width, height);
            // 设置投影矩阵
            gl.glMatrixMode(GL10.GL_PROJECTION);
            // 重置投影矩阵
            gl.glLoadIdentity();
            // 设置视口的大小
            gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
            //以下两句声明,以后所有的变换都是针对模型(即我们绘制的图形)
            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glLoadIdentity();
    
        }
    
        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            // 设置白色为清屏
            gl.glClearColor(1, 1, 1, 1);
    
        }
    }
    

    效果如下:

    显示效果

    5 几个重要的函数

    5.1 glVertexPointer()

    其实就是设置一个指针,这个指针指向顶点数组,后面绘制三角形(或矩形)根据这里指定的顶点数组来读取数据。 
    函数原型如下:

    void glVertexPointer(int size,int type,int stride,Buffer pointer)

     

    其中:

    • size: 每个顶点有几个数值描述。必须是2,3 ,4 之一。
    • type: 数组中每个顶点的坐标类型。取值:GL_BYTE,GL_SHORTGL_FIXEDGL_FLOAT
    • stride:数组中每个顶点间的间隔,步长(字节位移)。取值若为0,表示数组是连续的
    • pointer:即存储顶点的Buffer

    5.2 glColorPointer()

    跟上面类似,只是设定指向颜色数组的指针。 
    函数原型:

    void glColorPointer(
            int size,
            int type,
            int stride,
            java.nio.Buffer pointer
        );
    • size: 每种颜色组件的数量。 值必须为 3 或 4。
    • type: 颜色数组中的每个颜色分量的数据类型。 使用下列常量指定可接受的数据类型:GL_BYTE GL_UNSIGNED_BYTEGL_SHORT GL_UNSIGNED_SHORTGL_INT GL_UNSIGNED_INTGL_FLOAT,或 GL_DOUBLE
    • stride:连续颜色之间的字节偏移量。 当偏移量为0时,表示数据是连续的。
    • pointer:即颜色的Buffer

    5.3 glDrawArrays()

    绘制数组里面所有点构成的各个三角片。

    函数原型:

    void glDrawArrays(
        int mode,
        int first,
        int count
    );

    其中:

    • mode:有三种取值 
      • GL_TRIANGLES:每三个顶之间绘制三角形,之间不连接
      • GL_TRIANGLE_FAN:以V0 V1 V2,V0 V2 V3,V0 V3 V4,……的形式绘制三角形
      • GL_TRIANGLE_STRIP:顺序在每三个顶点之间均绘制三角形。这个方法可以保证从相同的方向上所有三角形均被绘制。以V0 V1 V2 ,V1 V2 V3,V2 V3 V4,……的形式绘制三角形
    • first:从数组缓存中的哪一位开始绘制,一般都定义为0
    • count:顶点的数量

    相关资料:

    【三维变换glMatrixMode】

    展开全文
  • openGL在android系统中只能全屏显示吗?可以控制让它只在屏幕的某一个部分显示
  • {//第一次调用时 为每个ASCII字符生成一个显示列表 isFirstCall = 0; //申请MAX__CHAR个连续的显示列表编号 lists = glGenLists(128); //把每个字符的绘制命令装到对应的显示列表中 wglUseFontBitmaps...
  • 利用OpenGL工具在VC环境下实现地球三维场景的显示,可以用鼠标键盘控制三维显示动作。
  • OpenGL显示时钟

    千次阅读 2014-05-21 18:55:43
    #include #include #include #include #include float hour; float minute; float second; int week; ...char* weekStr[] = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六",
  • Qt+OpenGL实现三维地形显示数字地图使用图片形式存储
  • 1.利用opengl对图片进行显示,点击图片画出一个角度,角度的线可以显示,但是角度的数字没有显示。我把文本的代码单独放到控制台显示显示是正常的,所以推断可能是图片的纹理 对文本的显示造成了干扰,哪些代码...
  • 前面复现篇的两篇文章中介绍了Qt+OpenGL框架下顶点和着色器及摄像机的知识,接下来我们用这两个知识来实现3D领域非常常见的任务—点云显示和交互。 点云的显示 3D领域常见的一个需求是将点云显示出来给用户,这个...
  • OpenGL实现3D立体显示

    千次阅读 2019-01-31 19:00:01
    OpenGL实现3D立体显示
  • Android 文字绘制到Bitmap上 OpenGL ES中似乎不能输出文本.将文本写到Bitmap上,再作为贴图,则可实现文字输出. 文字绘制到Bitmap上的方法为: String mstrTitle = "文字渲染到Bitmap!"; Bitmap bmp = Bitma
  • OpenGL(八) 显示列表

    千次阅读 2016-11-11 22:27:56
    OpenGL在即时模式(Immediate Mode)下绘图时,程序中每条语句产生的图形对象被直接送进绘图流水线,在显示终端立即绘制出来。当需要在程序中多次绘制同一个复杂的图像对象时,这种即时模式会消耗大量的系统资源,...
  • opengl+glfw+glew绘制英文字符和数字

    千次阅读 2019-04-04 20:51:14
    沿用上篇博文,加入了xyz轴的xyz文字显示 #include <iostream> #include <Windows.h> #define _USE_MATH_DEFINES #include <math.h> // GLEW #include <GL/glew.h> // GLFW #include...
  • 再上一篇里OBS 进阶之opengl使用方式 7 倒计时显示显示 三 二 一 倒计时后,再正式录制, 介绍了汉字 三 二 一 的显示,但是一般倒计时都是用的数字, 本节主要介绍数字 321的显示,不过使用的晶体管数字的...
  • 一、opengl显示 倒计时 1、矩形顶点数组 2、在指定相对位置绘制 3、在实际位置绘制 4、由相对位置获得实际位置 5、绘制 一 6、绘制 二 7、绘制 三 8、添加自己的渲染绘制 二、定时响应 难点有两个,一个是...
  • opengl教程ppt06-填充图形生成和字符显示.pdf
  • OPENGL 显示文字

    2012-11-13 20:45:11
    最近对opengl输出文字比较...在windows下wglUseFontBitmaps与wglUseFontOutlines提供opengl输出文字的字符资源,二者均通过构建一系列显示列表,然后用glCallLists执行这些显示列表就可以输出我们想要的字符。wglUseFo
  • OpenGL_参考手册.rar

    2019-08-21 14:25:15
    近年来,随着计算机技术的进步,我们跨入了一个三维时代,各种扣人心弦的三维游戏、能数字化地显示天气变化的气象服务、震撼人心的3D数字化特殊效果,无不使我们体验到三维世界的全新感觉。可视化、计算机动画、...
  • OpenGL基础绘制

    千次阅读 2019-09-24 12:58:48
    本文将会介绍使用OpenGL进行点,线,三角形乃至多边形的绘制。 下图是采用不同类型的图形效果 一、点 点”是一切的基础。 OpenGL提供了一系列函数指定一个点。它们都以glVertex开头,后面跟一个数字和1~2个...
  • 本文分析比较了颜色模型与光照模型,提出基于HSL颜色模型的海底地形多色渐变渲染与配色方法,基于OpenGL进行了真实感光照模型的设置,并分析了影响光照效果的相关因素,最终实现了以海底数字高程模型为数据基础并叠加...
  • 上一篇博客中,我们已经介绍了根据PCM解析出的...之前我们在绘制波形图采用的是循环拿出PCM中的数据传入OpenGL进行绘制,而要实现动态显示,这一点必须改变,我们需要将PCM中解析出的数据保存起来,实现方法是解析...
  • OpenGL入门教程

    万次阅读 多人点赞 2018-12-03 18:14:53
    OpenGL OpenGL是渲染2D、3D矢量图形硬件的一种软件接口。本质上说,它是一个3D图形和模型库,具有高度的可移植性,并且具有非常快的渲染速度。OpenGL并不是一种语言,而是更像一个C运行时函数库。它提供了一些预包装...
  • Opengles 显示文字 (Opengl ES笔记)

    千次阅读 2011-02-22 15:45:00
     使用OpengL ES 显示文字的实现 (Wince) <br />使用OpengL ES 显示文字对opengl es 初学者来说,可谓一个不大不小的麻烦。有人是利用了Opengl ES API封装了一些dll库来实现,例如glfont,不过...
  • Android平台上基于OpenGl渲染yuv视频

    千次阅读 2019-11-13 12:02:05
    这是我音视频专栏的第一篇实例解析,也算是入门篇,重点讲下如何使用OpenGl去渲染一个yuv视频。 本篇博文涉及的知识点主要有三个:1.yuv的概念 2.基于ndk进行C++程序的基本编写 3.OpenGl纹理的绘制 本文将重点讲知识...
  • 在先前的章节中,我们已经讨论OpenGL基本的一些渲染技术。这些基本的技巧足够渲染简单的图像,然而在渲染精细的画面逼真的画面的时候(非常多的顶点和纹理),如果使用之前的方式渲染(立即模式)速度就很慢了,考虑...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,854
精华内容 5,941
关键字:

opengl显示数字