首页 > 其他分享 >OpenGL ES 着色器(5)

OpenGL ES 着色器(5)

时间:2024-09-30 21:47:25浏览次数:8  
标签:片段 OpenGL ES 1f 顶点 GL 着色器 GLES30

OpenGL ES 着色器(5)

简述

着色器是在GPU上运行的程序,它会对每一个点都执行一次程序,并且计算出每个像素需要渲染的颜色,我们主要关注着色器的怎么传递数据,在OpenGL ES中,着色器传递数据分几种场景,一种是Cpu传递数据给GPU,一种是顶点缓冲区的数据传递到着色器,还有一种是顶点着色器数据传递给片段着色器。
着色器主要有三种类型传递数据参数:

  • attribute,用于顶点缓冲区数据传递到着色器中。
  • uniform,统一变量,由CPU将数据传递到GPU。
  • varying,用于顶点着色器数据向片段着色器传递。

着色器使用

我们以实现一个颜色渐变的圆形来介绍着色器的使用。
我们之前说过OpenGL渲染都是以三角形为基础的,那应该怎么渲染圆形呢,我的第一反应就是高数里微积分,通过无数个三角形拼接起来,不过这个方案有个问题,我们需要使用多少个三角形才合适,才能让圆形足够圆滑。
虽然GPU更善于执行计算,但是其实GPU也是可以处理逻辑的,所以其实只需要在着色器里做一个简单的逻辑就可以了。
所以我们的思路就是绘制一个正方形,在片段着色器里判断坐标,如果坐标在圆形范围内则为白色, 否则就配置透明的颜色,这样就可以渲染出一个圆形了。

在开始之前,我们需要了解用到的顶点着色器和片段着色器。

顶点着色器

顶点着色器主要用于获取顶点数据,一般是从顶点缓冲区中获取,里面有内置变量gl_Position作为顶点位置的输出,顶点着色器在读取顶点数据并且做预处理后,可以将坐标信息给gl_Position作为输出。
顶点着色器会对每一个顶点都执行一次。

片段着色器

片段着色器用于计算输出的颜色,有一个内置变量gl_FragColor,作为颜色的输出,片段着色器会对每一个像素都执行一次,然后返回该像素需要显示的颜色。
片段着色器可以获取从顶点着色器中获取数据,这里传递数据有两种模式,区别为是否插值,默认是平滑的,会插值的。(就是说几个顶点中的数据会根据坐标线性平滑的渐变,我们之前在顶点缓冲区章节的渐变色三角形demo就是这个原理)

我们想要实现圆形的判断逻辑,需要在片段着色器中处理逻辑,因为片段着色器会对每个像素执行一次。

顶点数据

我们顶点数据为四个顶点,每个顶点7个数据,前三个是坐标,后四个是颜色。
使用索引缓冲区,这里和之前绘制正方形的demo差不多,这里坐标覆盖了全屏幕,作用相当于画板,获取我们会通过一个统一变量来控制圆形半径。

private float[] vertexArray = new float[] {
        -1.0f, -1.0f, 0.0f, 1f, 1f, 1f, 1f,
        1.0f, -1.0f, 0.0f, 1f, 1f, 1f, 1f,
        -1.0f, 1.0f, 0.0f, 1f, 1f, 1f, 1f,
        1.0f, 1.0f, 0.0f, 1f, 1f, 1f, 1f
};

private short[] indexArray = new short[] {
        0,1,2,
        1,2,3
};

着色器代码

顶点着色器逻辑:
两个属性,vPosition是坐标,除了直接传给gl_Position外,还通过一个varying的变量传给片段着色器vertexPosition,我们说过这个参数传递默认是会插值的,所以片段着色器拿到的值相当于每个点的坐标。
属性inputColor是从顶点缓冲区中拿的数据,用作颜色,这里通过一个varying变量outputColor传给片段着色器。

private final String vertexShaderCode =
        "attribute vec4 vPosition;" +
                "attribute vec4 inputColor;" +
                "varying vec4 outputColor;" +
                "varying vec4 vertexPosition;" +
                "void main() {" +
                "  gl_Position = vPosition;" +
                "  vertexPosition = vPosition;" +
                "  outputColor = inputColor;" +
                "}";

片段着色器逻辑:
这里有两个传过来的参数,一个为vertexPosition,为像素点坐标位置,一个为outputColor,作为输出颜色。
还有一个统一变量radius作为圆形半径,我们以(0,0)为圆形,这里一个简单几何逻辑,x * x + y * y < radius * radius的部分就是圆形部分,属于圆形部分颜色输出outputColor,否则输出一个透明色。

private final String fragmentShaderCode =
        "precision mediump float;" +
                "varying vec4 vertexPosition;" +
                "varying vec4 outputColor;" +
                "uniform float radius;" +
                "void main() {" +
                "  if (vertexPosition.x * vertexPosition.x + vertexPosition.y * vertexPosition.y > radius * radius) {" +
                "    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);" +
                "  } else {" +
                "    gl_FragColor = outputColor;" +
                "  }" +
                "}";

顶点缓冲区数据填充

这里的逻辑和之前正方形绘制基本一样,就不细说了。

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // 清除颜色
    GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    // 创建顶点缓冲区
    int[] idBuffer = new int[2];
    GLES30.glGenBuffers(2, idBuffer, 0);
    vertexBufferId = idBuffer[0];
    elementBufferId = idBuffer[1];

    // 顶点缓冲区数据填充
    FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    vertexBuffer.put(vertexArray);
    vertexBuffer.position(0);

    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    GLES30.glBufferData(
            GLES30.GL_ARRAY_BUFFER,
            vertexArray.length * 4,
            vertexBuffer,
            GLES30.GL_STATIC_DRAW
    );
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);

    ShortBuffer indexBuffer = ByteBuffer.allocateDirect(indexArray.length * 4).order(ByteOrder.nativeOrder()).asShortBuffer();
    indexBuffer.put(indexArray);
    indexBuffer.position(0);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);
    GLES30.glBufferData(
            GLES30.GL_ELEMENT_ARRAY_BUFFER,
            indexArray.length * 4,
            indexBuffer,
            GLES30.GL_STATIC_DRAW
    );
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);

    // shader
    shaderProgramId = initShaderProgram(vertexShaderCode, fragmentShaderCode);
}

顶点布局以及渲染

配置顶点缓冲区布局,前三个数作为坐标,后4个数作为颜色。
设置统一变量radius,作为圆形半径。

public void onDrawFrame(GL10 gl) {
    // 清除屏幕
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
    // 使能着色器程序
    GLES30.glUseProgram(shaderProgramId);

    // 顶点缓冲区每个顶点前3个数作为坐标
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    int positionLocation = GLES30.glGetAttribLocation(shaderProgramId, "vPosition");
    GLES30.glEnableVertexAttribArray(positionLocation);
    GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 7 * 4, 0);

    // 顶点缓冲区每个顶点后4个数作为颜色
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    int inputColorLocation = GLES30.glGetAttribLocation(shaderProgramId, "inputColor");
    GLES30.glEnableVertexAttribArray(inputColorLocation);
    GLES30.glVertexAttribPointer(inputColorLocation, 4, GLES30.GL_FLOAT, false, 7 * 4, 3 * 4);

    // 配置统一变量,圆形半径
    int radiusLocation = GLES30.glGetUniformLocation(shaderProgramId, "radius");
    GLES30.glUniform1f(radiusLocation, 0.5f);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);
    // 调用DrawCall绘制三角形
    GLES30.glDrawElements(GLES30.GL_TRIANGLES, 6, GLES30.GL_UNSIGNED_SHORT, 0);

    // 清除配置
    GLES30.glDisableVertexAttribArray(positionLocation);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
    GLES30.glUseProgram(0);
}

效果

在这里插入图片描述

小结

看完本节的demo,应该对着色器的使用有了一定的,着色器的语法和C语言有一些类似,不过它其实不是C语言,而是自定义的语言,然后会编辑成显卡驱动可以理解的字节码。
OpenGL ES章节更多的都是以一些简单的demo来介绍,这样会更加容易理解,后面我们会介绍纹理,也会需要在片段着色器中做处理。

标签:片段,OpenGL,ES,1f,顶点,GL,着色器,GLES30
From: https://blog.csdn.net/weixin_43833152/article/details/142664505

相关文章

  • Windows下安装Nessus 10.8.3安装破解教程
    1、下载:下载地址:https://www.tenable.com/downloads/nessus浏览器访问https://127.0.0.1:8834重点:Registeroffline,选择“ManagedScanner”,再选择“Tenablesecuritycenter”,最后一步设置账号密码,账号密码没要求。​​2、获取插件包2.1在命令行模式下(管理员身份运行),......
  • 怎么解决os.system中Program Files中空格报错的问题
    在通过os.system(PATH)时由于c盘中的ProgramFiles之间带有空格,从而导致在识别时会识别到Program时停止从而产生报错例如:os.system('C:\ProgramFiles(x86)\Microsoft\Edge\Application\msedge.exe')通过网上查找原因,大部分都是说加上双引号就行了我以为是:"C:\ProgramF......
  • C#做MES中FTP代码怎么写?客户端,服务端如何安装测试?
    一、介绍FTP免费开源软件1:FileZilla是一个免费开源的FTP软件,分为客户端版本和服务器版本,且具备所有的FTP软件功能。具有可控性,有条理的界面和管理多站点的简化方式,是的Filezilla客户端版本成为一个方便高效的FTP客户端工具,而Filezilla服务端版本则是一个小巧并且可靠支持FTP&......
  • AtCoder Beginner Contest 365题解
    A-LeapYear按照题意模拟即可。codeB-SecondBest按照题意模拟即可。codeC-TransportationExpenses考虑当\(x\)增大时,\(\min(x,a_i)=x\)的项会越来越少。换言之,当\(x\)足够大时,\(ans=\suma_i\),若此时\(ans>M\)则说明无论补贴多少,这时答案都是一定的......
  • Hard Process
    HardProcess(题面)大意:给定一个长度为\(n(0<=n<=10^5)\)的序列,序列中只包含0或1,现有k次机会可以将0改为1,问,k次机会前最长连续1序列的长度并且输出这个序列(只需一个)。解法:二分答案+前缀和证二分答案的单调性:先解释check函数:现有一个需要查询的长度len,从1开始直到n,遍历每一个长......
  • WPF Calendar DisplayMode SelectionMode FirstDayOfWeek Start End BlackoutDates
    //xaml<Windowx:Class="WpfApp427.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mi......
  • codeforces round 975 E(div.2)(lambda表达式实现dfs,min_element函数,一定要优化重复的
    解题历程:看到题目要求要用最少的消除次数让所有叶子深度相同,通过观察,那么就只需要将所有深度都尝试一遍就行了,可是我当时没多想就用dfs记录所有节点的深度,单独将所有叶子和该叶子的深度存起来,记录最大的深度,从最大深度尝试到深度0,对于深度小于当前尝试深度的叶子,用dfs的方式将与......
  • android开发Execution failed for task ':bundleDebugAar'...Direct local .aar file
    1.问题描述[+103ms]FAILURE:Buildfailedwithanexception.[]*Whatwentwrong:[]Executionfailedfortask':jpush_flutter:bundleDebugAar'.[]>Errorwhileevaluatingproperty'hasLocalAarDeps'oftask......
  • Paper Reading: Deep balanced cascade forest: An novel fault diagnosis method for
    目录研究动机文章贡献本文方法混合采样新型平衡森林DBCF整体流程实验结果数据集和实验设置对比故障诊断方法对比基于决策树的方法对比不平衡分类方法模型效率的比较优点和创新点PaperReading是从个人角度进行的一些总结分享,受到个人关注点的侧重和实力所限,可能有理解不到位的......
  • AtCoder Beginner Contest 371(ABCDE)
    A个人直接硬解,讨论情况也并不复杂代码:#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e6+10;voidsolve(){chara,b,c;cin>>a>>b>>c;if(a=='<'){if(c=='<......