1.OpenGLES——FBO方式的离屏渲染

2023-11-02

参考

https://blog.csdn.net/u011371324/article/details/78011211

 

书写本文的初衷是为了自我学习

 

出现背景

    明确两个概念

  • 窗口系统默认帧缓冲

  • 应用程序帧缓冲

    FBO(frame buffer object),帧缓冲区对象,在Android中,绘制三角形一般都是直接重写GLSurfaceView,因为Android已经集成好了OpenGLES的环境,渲染操作都是在默认的帧缓冲去做的,这个帧缓冲是我们在创建一个Surface的时候自动创建的(Surface,第二节讲一下)。但这仅限于demo级别,实际应用中,如果我们需要渲染到纹理,往往不使用窗口系统默认提供的帧缓冲区域,需要自己创建了。

何为帧缓冲?

    显示到屏幕上的每一帧数据其实对应的就是内存中的数据,在内存中对应分配着存储帧数据的缓冲区,包括写入颜色的颜色缓冲,写入深度值的深度缓冲,以及基于一些条件丢弃片元的模板缓冲,这几种缓冲一起称之为帧缓冲。

 

为什么要使用帧缓冲?

之前绘制使用的纹理都是使用图片加载得到的纹理,如果我们要对纹理在着色器中做一些处理,模糊、虚化、双屏、镜子等特效,那么使用帧缓冲可以很好的实现。此外,帧缓冲提供了一个很高效的机制,它能够快速的分离和附着纹理或渲染缓冲对象,这比在帧缓冲之间切换要快得多。

两种附件

纹理附件

纹理附件和通过图片加载的纹理类似,只不过这个纹理附加是通过渲染命令写入到纹理当中的,不是通过图片纹理得到的。

注意:除了附加颜色附件之外,还可以附件深度和模板纹理附件。例如,当我们开启了深度测试时,就需要附着一个深度附件,来表示深度信息,以便进行深度测试。为了附加一个深度缓冲,可用GL_DEPTH_ATTACHMENT作为附件类型。

 

渲染缓冲对象(RBO

渲染缓冲对象(RenderBuffer Object,简称RBO)是一个OpenGL格式的缓冲,即以OpenG原生(native)格式存储它的数据,因此它相当于是优化过的内部数据。当它绑定到FrameBuffer上时,渲染的像素信息就写到RBO中。

渲染缓冲对象将渲染数据存储到缓冲中,并且以原生格式存储,所以它成为一种快速可写入的介质。但是,只能写入,不能修改。RBO常常用来存储深度和模板信息,用于深度测试和模板测试,而且比用纹理存储的深度和模板方式要快得多。RBO可以用来实现双缓冲(double buffer)。

同样,渲染缓冲对象也可以写入颜色信息,然后将图像信息显示在屏幕上。

貌似RBO比纹理有点多,但也不是万能的,纹理自有纹理的优点,纹理能够在shader中进行操作或者需要读取像素时,做一些处理,此时RBO就无能为力了。

案例演示

网上的很多教程都只是说明了流程,然后演示的demo都混杂着各种其他的东西,初学者要费很大的力气才能理解。下面说明一个单纯只应用了FBO的案例:

环境是使用Android集成好的,GLSurfaceView ,egl环境也是。(关于这一点,可以看下一个文档,介绍EGL)。

撇开OpenGLES的API,单单说离屏渲染的流程如下:

  1. 创建一个帧缓冲的buffer,取名为FrameBuffer(一块显存)。

  2. 将要绘制的纹理数据写入FrameBuffer(即绑定)。

  3. 在渲染的任意时刻将帧缓冲FrameBuffer中的数据取出使用(在你想用的任意时刻)。

 

对比直接绘制到屏幕的渲染流程如下:

  1. 将要绘制的纹理数据写入默认的帧缓冲,取名defaultFrameBuffer。

  2. 在渲染的过程中连续的将defaultFrameBuffer中的数据取出使用。

我们只是使用OpenGLES的API去完成上面的流程。

首先是工程结构,FBO的实现应用了红线的几个文件(其他文件绘制了一个普通的三角形,):

MainActivity指定了View。

public class MainActivity extends AppCompatActivity {

    private GLSurfaceView mGLView;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        mGLView = new MySurfaceView(this);

        setContentView(mGLView);

    }

}

 

MySurfaceView指定了渲染器,渲染模式。onDrawFrame开始离屏渲染。

public class MySurfaceView extends GLSurfaceView {

    private MyRenderer mRenderer;

    private FBORenderer fboRenderer;

    public MySurfaceView (Context context){

        super(context);

        this.setEGLContextClientVersion(2);

//      绘制普通三角形的渲染器

//      mRenderer=new MyRenderer(context);

//      this.setRenderer(mRenderer);

        fboRenderer = new FBORenderer(context);

        this.setRenderer(fboRenderer);

        this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

    }

}

FBORenderer重写了GLSurfaceView的渲染器

public class FBORenderer implements GLSurfaceView.Renderer{

    public static int sScreenWidth;

    public static int sScreenHeight;

    private Shape_FBO mRectangle;

    float yAngle;

    float xAngle;

    private Context mContext;

    public FBORenderer(Context context) {

        super();

        mContext = context;

    }

 

 

    @Override

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1);

        mRectangle = new Shape_FBO(mContext);

        GLES20.glEnable(GLES20.GL_DEPTH_TEST);

    }

    @Override

    public void onSurfaceChanged(GL10 gl, int width, int height) {

        sScreenWidth = width;

        sScreenHeight = height;

        GLES20.glViewport(0, 0, width, height);

        Matrix.perspectiveM(mProjectionMatrix, 0, 45, (float)width/height, 2, 5);

        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3,  0, 0, 0, 0, 1, 0);

    }

    private final float[] mProjectionMatrix = new float[16];

    private final float[] mViewMatrix = new float[16];

    private final float[] mModuleMatrix = new float[16];

    private final float[] mViewProjectionMatrix = new float[16];

    private final float[] mMVPMatrix = new float[16];

    @Override

    public void onDrawFrame(GL10 gl) {

        Matrix.setIdentityM(mModuleMatrix, 0);

        Matrix.rotateM(mModuleMatrix, 0, xAngle, 1, 0, 0);

        Matrix.rotateM(mModuleMatrix, 0, yAngle, 0, 1, 0);

        Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

        Matrix.multiplyMM(mMVPMatrix, 0, mViewProjectionMatrix, 0, mModuleMatrix, 0);

//        GLES20.glViewport(0, 0, 1024, 1024);

        mRectangle.draw(mMVPMatrix, mModuleMatrix);

        mRectangle.draw(mMVPMatrix, mModuleMatrix);

    }

}

本类就是核心的绘制类了。

1.colorTextureId这个纹理缓存指定到FrameBuffer,与FrameBuffer中的数据进行关联,也就是说,现在colorTextureId就是FrameBuffer中数据所生成的图片。

2.在FrameBuffer中绘制读入的图片mLoadedTextureId。

3.在默认的窗口defaultFrameBuffer中绘制colorTextureId。

对照代码理解这张图,更有体会。

一般情况我们都是直接走红线进行绘制,FBO离屏渲染走绿线

可以在项目中crtl+f( GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTxtureId);

将其中的colorTxtureId,替换为mLoadedTextureId,

并注释draw方法中/*================================================================*/以上的代码,你也会看到图片显示了出来。

首先是生成一张纹理mLoadedTextureId,然后绑定到FrameBuffer中,然后

public class Shape_FBO {

    private static String TAG = "ShapeFBO";

    private FloatBuffer mSqureBuffer;

    private FloatBuffer mSqureBufferfbo;

    private int mFrameBufferProgram;

    private int mWindowProgram;

    private int mLoadedTextureId;

    private Context mContext;

 

 

    public Shape_FBO(Context context) {

        this.mContext = context;

        this.initVetexData();

    }

    public void initVetexData() {

        //生成纹理

        mLoadedTextureId=initTexture(R.drawable.texture1);

        //准备绘制数据

        float [] bgVertex = new float[] {

                -1f,-1f,  0,1,

                -1f,1f,  0,0,

                1f,-1f,  1,1,

                1f,1f,  1,0

        };

        ByteBuffer vbb0 = ByteBuffer.allocateDirect(bgVertex.length * 4);

        vbb0.order(ByteOrder.nativeOrder());

        mSqureBuffer = vbb0.asFloatBuffer();

        mSqureBuffer.put(bgVertex);

        mSqureBuffer.position(0);

 

 

        float [] fboVertex = new float[] {

                -1f,-1f,  0,1,

                -1f,1f,  0,0,

                1f,-1f,  1,1,

                1f,1f,  1,0

        };

        ByteBuffer vbb1 = ByteBuffer.allocateDirect(fboVertex.length * 4);

        vbb1.order(ByteOrder.nativeOrder());

        mSqureBufferfbo = vbb1.asFloatBuffer();

        mSqureBufferfbo.put(fboVertex);

        mSqureBufferfbo.position(0);

    }

    public int initTexture(int res) {

        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), res);

        int [] textures = new int[1];

        GLES20.glGenTextures(1, textures, 0);

        //绑定纹理缓存到纹理单元

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);

        //设置采样,拉伸方式

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);

        //指定纹理图片生成2D纹理

        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

        //释放bitmap

        bitmap.recycle();

        //解除绑定

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

        return textures[0];

    }

    public void draw(float[] mvpMatrix, float[] mMatrix) {

        // 生成FrameBuffer

        int [] framebuffers = new int[1];

        GLES20.glGenFramebuffers(1, framebuffers, 0);

        // 生成Texture

        int [] textures = new int[2];

        GLES20.glGenTextures(2, textures, 0);

        int colorTxtureId = textures[0];

        //绑定纹理缓存到纹理单元

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTxtureId);

        //设置采样,拉伸方式

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);

        //生成2D纹理

        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, FBORenderer.sScreenWidth, FBORenderer.sScreenHeight,0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, null);

        //绑定framebuffer

        int framebufferId = framebuffers[0];

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferId);

        //挂载textureID到framebufferId

        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, colorTxtureId, 0);

        if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)== GLES20.GL_FRAMEBUFFER_COMPLETE) {

            Log.e("shapefbo", "glFramebufferTexture2D error");

        }

        int frameBufferVertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);

        int frameBufferFagmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        mFrameBufferProgram = GLES20.glCreateProgram();

        GLES20.glAttachShader(mFrameBufferProgram, frameBufferVertexShader);

        GLES20.glAttachShader(mFrameBufferProgram, frameBufferFagmentShader);

        GLES20.glLinkProgram(mFrameBufferProgram);

        int positionHandle1 = GLES20.glGetAttribLocation(mFrameBufferProgram, "aPosition");

        int textureCoordHandle1 = GLES20.glGetAttribLocation(mFrameBufferProgram, "aTextureCoord");

        int textureHandle1 = GLES20.glGetUniformLocation(mFrameBufferProgram, "uTexture");

        mSqureBufferfbo.position(0);

        GLES20.glVertexAttribPointer(positionHandle1, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBufferfbo);

        mSqureBufferfbo.position(2);

        GLES20.glVertexAttribPointer(textureCoordHandle1, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBufferfbo);

        GLES20.glEnableVertexAttribArray(positionHandle1);

        GLES20.glEnableVertexAttribArray(textureCoordHandle1);

//        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mLoadedTextureId);

        GLES20.glUniform1i(textureHandle1, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);

 

 

        /*================================================================*/

        // 切换到窗口系统的缓冲区

        GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

        int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, windowVertexShaderCode);

        int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, windowFragmentShaderCode);

        mWindowProgram = GLES20.glCreateProgram();

        GLES20.glAttachShader(mWindowProgram, vertexShader);

        GLES20.glAttachShader(mWindowProgram, fragmentShader);

        GLES20.glLinkProgram(mWindowProgram);

        GLES20.glUseProgram(mWindowProgram);

        int positionHandle = GLES20.glGetAttribLocation(mWindowProgram, "aPosition");

        int textureCoordHandle = GLES20.glGetAttribLocation(mWindowProgram, "aTextureCoord");

        int textureHandle = GLES20.glGetUniformLocation(mWindowProgram, "uTexture");

        mSqureBuffer.position(0);

        GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBuffer);

        mSqureBuffer.position(2);

        GLES20.glVertexAttribPointer(textureCoordHandle, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBuffer);

        GLES20.glEnableVertexAttribArray(positionHandle);

        GLES20.glEnableVertexAttribArray(textureCoordHandle);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTxtureId);

        GLES20.glUniform1i(textureHandle, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        GLES20.glDeleteTextures(2, textures, 0);

        GLES20.glDeleteFramebuffers(1, framebuffers, 0);

    }

 

 

    private int loaderShader(int type, String shaderCode) {

        int shader = GLES20.glCreateShader(type);

        GLES20.glShaderSource(shader, shaderCode);

        GLES20.glCompileShader(shader);

        return shader;

    }

    private String windowVertexShaderCode = ""

            + "attribute vec2 aPosition;"

            + "attribute vec2 aTextureCoord;"

            + "varying vec2 vTextureCoord;"

            + "void main(){"

            + "gl_Position = vec4(aPosition,0,1);"

            + "vTextureCoord = aTextureCoord;"

            + "}";

    private String windowFragmentShaderCode = "precision mediump float;"

            + "uniform sampler2D uTexture;"

            + "varying vec2 vTextureCoord;"

            + "void main(){"

            + "gl_FragColor = texture2D(uTexture, vTextureCoord);"

            + "}";

    private String vertexShaderCode = ""

            + "attribute vec2 aPosition;"

            + "attribute vec2 aTextureCoord;"

            + "varying vec2 vTextureCoord;"

            + "void main(){"

            + "gl_Position = vec4(aPosition,0,1);"

            + "vTextureCoord = aTextureCoord;"

            + "}";

    private String fragmentShaderCode = "precision mediump float;"

            + "uniform sampler2D uTexture;"

            + "varying vec2 vTextureCoord;"

            + "void main(){"

            + "gl_FragColor = texture2D(uTexture, vTextureCoord);"

            + "}";

GitHub地址欢迎点星星

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

1.OpenGLES——FBO方式的离屏渲染 的相关文章

  • LWJGL 窗口具有透明背景?

    我想创建一个没有 黑色背景 区域的窗口 但您可以看到任何其他打开的窗口等 也就是说 渲染场景并且仅渲染场景 不留框架 不留背景区域 我读过一种方法 该方法涉及渲染到隐藏的 OpenGL 窗口并将其缓冲在内存中 创建透明分层窗口以及从内存复制
  • glDrawElements 在 PyOpenGL 中绘制立方体

    我最近开始通过 Python 学习 OpenGL 这要归功于几个教程 尤其是 Nicolas P Rougier 的教程 http www labri fr perso nrougier teaching opengl http www l
  • 将 CVPixelBuffer 渲染到 NSView (macOS)

    我有一个CVPixelBuffer我正在尝试在屏幕上有效地绘制 转变为低效率的方式NSImage可以工作 但速度非常慢 丢掉了大约 40 的帧数 因此 我尝试使用将其渲染在屏幕上CIContext s drawImage inRect fr
  • OpenGL NURBS 曲面

    我正在学习 OpenGL 我想要一个中间有轻微驼峰的表面 我目前正在使用这段代码 但我不确定如何调整 ctrl 点以使其达到我想要的方式 它目前就像 我想要这样的 我不完全确定我应该使用哪些控制点 并且我对其工作原理感到困惑 include
  • 哪个对缓存最友好?

    我试图很好地掌握面向数据的设计以及如何在考虑缓存的情况下进行最佳编程 基本上有两种情况我无法完全确定哪个更好以及为什么 是拥有一个对象向量更好 还是拥有对象原子数据的多个向量更好 A 对象向量示例 struct A GLsizei mInd
  • glutPostRedisplay 不在循环内工作

    我试图让一个人在 y 轴上跳跃 所以我使用 2 秒的循环 第一秒它应该向下移动并弯曲膝盖 第二秒它应该向上移动 然后在起始位置完成 现在我刚刚开始让这个人在第一秒内跪下并弯曲膝盖 我还没有编写动画的其余部分 问题是 glutPostRedi
  • OpenGL 和加载/读取 AoSoA(混合 SoA)格式的数据

    假设我有以下 AoSoA 格式的简化结构来表示顶点或点 struct VertexData float px 4 position x float py 4 position y 也就是说 每个实例VertexData存储4个顶点 我见过的
  • OpenGL:调试“单通道线框渲染”

    我正在尝试实现论文 单通道线框渲染 它看起来很简单 但它给了我所期望的厚暗值 论文没有给出计算海拔高度的确切代码 所以我按照自己认为合适的方式进行了操作 代码应该将三个顶点投影到视口空间中 获取它们的 高度 并将它们发送到片段着色器 片段着
  • 使用 glDrawElements 时在 OpenGL 核心配置文件中选取三角形

    我正在使用 glDrawElements 绘制三角形网格 并且希望能够使用鼠标单击来拾取 选择三角形 三角形的网格可以很大 在固定功能 OpenGL 中 可以使用 GL SELECT http content gpwiki org inde
  • openGL转png

    我正在尝试将包含大量纹理 没有移动 的 openGL 编辑 我画的卡片 thx unwind 转换为一个 PNG 文件 我可以在框架的另一部分中使用该文件我正在与 有 C 库可以做到这一点吗 thanks 如果您的意思只是 获取由 Open
  • glEnableVertexAttribArray 中“index”参数的含义以及(可能)OS X OpenGL 实现中的错误

    1 我是否正确理解 要使用顶点数组或VBO进行绘制 我需要所有属性在着色器程序链接之前调用glBindAttribLocation 或者在着色器程序成功链接后调用glGetAttribLocation 然后使用glVertexAttribP
  • WebKit 是否使用 OpenGL 来渲染 CSS 过渡?

    WebKit 是使用 OpenGL 来渲染 CSS 过渡 还是使用软件渲染 WebKit 只是一个前端 这取决于后端和硬件支持 谷歌浏览器使用skia http code google com p skia 作为后端 它可以使用软件或硬件
  • 使用 OpenGL 着色器进行数学计算 (C++)

    我有一个矩阵 例如 100x100 尺寸 我需要对每个元素进行计算 matrix i j tt 8 5例如 我有一个巨大的矩阵 我想使用 OpenGL 着色器来实现该算法 我想使用着色器 例如 uniform float val unifo
  • 使用 C# 截取任何外部应用程序的屏幕截图

    我们有一个 C WPF 应用程序 我们想要在其中截取我们启动的任意应用程序的屏幕截图 即 我们可以引用我们启动的进程 应用程序可能已最小化或位于其他窗口后面 但我们仍然只需要单个应用程序的图像 而不是重叠像素 我知道使用 BitBlt 或的
  • PyQt5 的 OpenGL 模块和版本控制问题(调用不正确的 _QOpenGLFunctions_(ver))

    我一直在努力得到PyQt5 helloGL 示例代码 https github com baoboa pyqt5 blob master examples opengl hellogl py编译 当我尝试构建解决方案时 我得到 Traceb
  • OpenGL - 两个纹理的幂

    OpenGL 使用二次幂纹理 这是因为由于 MipMapping 某些 GPU 只接受 2 的幂纹理 当绘制比实际更大的纹理时 使用这些二次方纹理会导致问题 我想到了一种方法来解决这个问题 即仅在我们使纹理小于实际大小时使用 PO2 比率
  • GLSL NVidia 方形神器

    当 GLSL 着色器在以下 GPU 上生成不正确的图像时 我遇到了问题 GT 430 GT 770 GTX 570显卡760 但在这些上正常工作 英特尔高清显卡 2500英特尔高清4000英特尔4400显卡740MRadeon HD 631
  • 在 Linux 上运行我自己的程序的权限被拒绝? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有Ubuntu 9 4 我已经构建了程序 一些基本的 OpenGL 该程序只是制作一个旋转的正方形 然后运行它并 sh blabla p
  • OpenGL缓冲区更新[重复]

    这个问题在这里已经有答案了 目前我正在编写一个模拟水的程序 以下是我所做的步骤 创建水面 平面 创建VAO 创建顶点缓冲区对象 在其中存储法线和顶点 将指针绑定到此 VBO 创建索引缓冲区对象 然后我使用 glDrawElements 渲染
  • 在 2D 纹理上绘制的红色矩形在绘制后立即消失

    跟随我的另一个问题 https stackoverflow com questions 18477291 render an outlined red rectangle on top a 2d texture in opengl 1847

随机推荐

  • 【简易心电测量电路(AD620)】

    学生版低成本心电测量 1 背景 1 1 心电图产生原理 1 2 心电信号特征 1 3 项目总体设计 2 电路设计 2 1前级放大与右腿驱动电路 2 2 低通滤波电路 2 3 50Hz陷波电路 2 4 后置放大电路 3 部分测试 3 1 前级
  • 以太坊原理分析(二)以太坊区块结构和账户体系

    1 引言 区块链的本质是一个分布式的数据库 因此不同时刻的用户数据的写入对应着不同的状态 比特币使用UTXO来表示状态的转移 而以太坊使用账来表示状态的转移 2 账户 以太坊中存在两种账户 分别是外部账户和合约账户 外部账户EOA 一般自然
  • js json数组获取最大、最小值

    var array index id 119 area id 18335623 name 满意度 value 100 index id 119 area id 18335624 name 满意度 value 20 inde
  • linux命令查看tcp流量,linux中查看系统活动情况报告sar命令详解

    有很多工具可以看网络流量 但我最喜欢sar sar System Activity Reporter系统活动情况报告 是目前 Linux 上最为全面的系统性能分析工具之一 可以从多方面对系统的活动进行报告 包括 文件的读写情况 系统调用的使
  • hbase 压缩配置

    对hbase 的hfile 进行snappy压缩的配置 1 gt hadoop集群必须先配置好snappy压缩 参考http hymanliu iteye com blog 2224330 2 gt 在hbase的lib native目录下
  • 数据挖掘基础学习笔记

    文章目录 一 数据挖掘基础 1 1 数据挖掘基础环境安装 1 2 Jupyter Notebook 1 2 1 介绍 1 2 2 快速使用 二 Matplotlib画图 2 1 Matplotlib简介 2 2 Matplotlib三层结构
  • mysqlz中navicat出现1064错误

    1064错误一般就是语法错误 如下 但是又并不很常见 当出现时你可能找半天也找不到 在这里我将说明我所遇到的很隐蔽的错误 当使用外键约束时 外键约束上方的 id 与下方拼写的 外键约束代码 之间是不加 的
  • 各种光源(灯)的光谱

    1 荧光 灯350 700nm 一般的 荧光 灯的荧光粉采用三波长荧光粉的不同类型灯管所发出光的分布 说简单就是452n 533nm 611nm 三个波长 2 白炽灯 基本在400nm 由于白炽灯是钨丝的热效应发光 波长主要集中在可见光偏红
  • java多线程使用详解与案例,超详细

    文章目录 线程lamda表达式方式启动 简单 常用 java使用多线程的三种方式 继承Thread 实现Runnable 实现Callable 线程池的使用 守护线程 使用lamda表达式简化java的书写简单化案例如下 多线程综合案例 1
  • Java 连接池 用尽_连接池已经耗尽的表现是什么?解决方法是什么?

    作为程序员的话难免经常跟数据库打交道 了解连接池倒是也不奇怪 进而知道连接池也正常 那么你是否清楚连接池已经消耗殆尽的表现是什么吗 一般怎么解决呢 有时候在通过其中一台数据库客户端连接访问服务器上的DB 所有对象 表 视图 存储过程 都打不
  • maven异常:Missing artifact com.microsoft.sqlserver:sqljdbc4:jar:4.2

    好像用依赖的方式将上面的jar包导入prom文件 idea会报错 下载该jar包会失败 两个解决方案 方案一 1 下载sqljdbc42 jar包 官网地址 https www microsoft com en us download de
  • Obliv-C使用详解(混淆电路)

    前期说明 Obliv C是一款GCC包装器 其作者在C语言的基础上对其进行了一定的类C处理 添加了一些规则限制 用于模拟实现混淆电路 Obliv C不需要手动混淆 只需要调用其中的函数便可实现混淆电路 其不涉及真实硬件电路仿真 即无法导出函
  • 安卓期末考试

    考点 第二章 第三章 核心点 第二章最后讲了一个线程 必须要会 第四章了解 第五第六第七了解 第九章三个存储 简单存储会写 文件存储 数据库存储 第十章不会考很多 核心也是线程 安卓 移动应用开发 考试 选择题 Logcat e error
  • ElasticSearch第二十讲 Elasticsearch的优势和应用场景

    Elasticsearch简介 Elasticsearch 是一个开源的分布式搜索和分析引擎 构建在 Apache Lucene 基础之上 它提供了一个快速 可扩展和具有强大查询功能的分布式搜索解决方案 Elasticsearch 被广泛应
  • javascript中with的用法

    关键字with 任何对象都可以使用with 通过使用with可以减少写一点代码 使用方法如下 1 语法 with object jsvascript代码 参数 一个对象 必须是一个对象 假设我们传入了一个obj对象 用法 花括号中的 jsv
  • 软件测试学习记录

    1 软件测试的定义 使用技术的手段验证软件是否满足需求 2 软件测试分类 按阶段分类 单元测试 针对源代码进行测试 集成测试 针对接口进行测试 系统测试 针对功能和非功能进行测试 验收测试 内测 公测 按代码可见度划分 黑盒测试 不关注源代
  • 北斗+车载录像机:“两客一危”的黑匣子

    两客一危 是指从事旅游的包车 三类以上班线客车和运输危险化学品 烟花爆竹 民用爆炸物品的道路专用车辆 由于该类车辆的危险系数较高 事故概率也比普通车辆大 各地方地区已就此类车辆陆续出台相应政策 例如早在2011年湖南省交通运输厅明确指出 两
  • 复制代码如何处理前面的行号

    网上类似的例子很多 1 一个很懒的方法 2 文本编辑器中正则表达式替换 一 一种情况是行号后带 输入 0 9 或者输入 0 9 0 9 0 9 二 第二种情况是行号数字后带空格 用ultraEdit的替换功能 正则表达式 1 9 0 9 注
  • Debian10搭建bind9主服务器搭建

    文章目录 1 环境 2 任务描述 3 设置IP地址以及dns 4 安装bind9服务 5 修改配置文件 6 测试 7 易错总结 1 环境 IPaddress 192 168 1 1 netmask 255 255 255 0 getaway
  • 1.OpenGLES——FBO方式的离屏渲染

    参考 https blog csdn net u011371324 article details 78011211 书写本文的初衷是为了自我学习 出现背景 明确两个概念 窗口系统默认帧缓冲 应用程序帧缓冲 FBO frame buffer