片段着色器

作者: cain_huang | 来源:发表于2017-07-02 21:23 被阅读64次

OpenGLES可编程管线流程:


可编程管线流程

一、固定功能片段着色器
在OpenGLES 1.1中(固定功能桌面OpenGL),可以使用一组有限的方程式,确定如何组合片段着色器的不同输入。可以使用三种输入:插值顶点颜色、纹理颜色和常量颜色。顶点颜色通常保存一个预先计算的颜色或顶点照明计算的结果。纹理颜色来自于使用图元纹理坐标绑定的纹理中读取的值,而常量颜色可以对每个纹理单元设置。组合这些输入相当有限,输入的A、B、C可能来自于顶点颜色、纹理颜色或常量颜色,组合能够实现大量有趣的特效,但是比起可编程管线有很大的距离。可用的方程式如下图所示

RGB组合函数

二、片段着色器概述
片段着色器为片段提供了通用功能的可编程方法。输入部分包括:
输入 —— 顶点着色器生成的插值数据
统一变量 —— 片段着色器使用的状态,常量值
采样器 —— 用于访问着色器中的纹理图像
代码 —— 片段着色器源代码或二进制代码,描述在片段上执行的操作

片段着色器的输入输出如下:


片段着色器输入输出

内建特殊变量:
gl_FragCoord —— 片段着色器中的只读变量。保存片段的窗口相对坐标
gl_FrontFacing —— 片段着色器中的一个只读变量。表示片段是否是正面图片的一部分
gl_PointCoord —— 只读变量,渲染点时使用。
gl_FragDepth —— 只写输出变量,在片段着色器中写入时,覆盖片段的固定功能深度值。慎用,因为可能会导致GPU深度优化禁用,即“Early-Z”功能被禁用。“Early-Z”会使得不能通过深度测试的片段不会被着色。

内建常量
gl_MaxFragmentInputVectors —— 片段着色器输入的最大数量,15
gl_MaxTextureImageUints —— 可用纹理图像单元的最大数量,16
gl_MaxFragmentUniformVectors —— 片段着色器内可以使用的vec4统一变量项目的最大数量,224
gl_MaxDrawBuffers —— 多重渲染目标(MRT)的最大支持数量,4
gl_MinProgramTexelOffset/gl_MaxProgramTexelOffset —— 通过内建ESSL函数 texture * Offset() 便宜参数支持的最小和最大偏移量,分别是 -8 和 7
可以通过glGetIntegerv 来查询

精度限定符
片段着色器中没有默认精度,因此每个片段着色器程序都必须声明一个默认精度。

三、多重纹理
多重纹理用于组合多个纹理贴图。下面来介绍一下多重纹理的用法
顶点着色器代码如下:

#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
void main() {
    gl_Position = a_position;
    v_texCoord = a_texCoord;
}

片段着色器代码如下:

#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_baseMap;
uniform sampler2D s_lightMap;
void main() {
    vec4 baseColor;
    vec4 lightColor;

    baseColor = texture(s_baseMap, v_texCoord);
    lightColor = texture(s_lightMap, v_texCoord);
    outColor = baseColor * (lightColor + 0.25);
}

意思就是将两个texture进行矩阵相乘合并。
加载Texture代码如下:

typedef struct {
    GLuint programObject;

    GLint baseMapLoc;
    GLint lightMapLoc;

    GLuint baseMapTexId;
    GLuint lightMapTexId;

} UserData;


GLuint loadTexture(void *ioContext, char *fileName) {

    int width, height;
    char *buffer = loadTGA(ioContext, fileName, &width, &height);
    GLuint textureId;

    if (buffer == NULL) {
        ALOGE("Error loading (%s) image.\n", fileName);
        return 0;
    }

    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    free(buffer);

    return textureId;
}

上述代码就是生成一个Texture,并加载TGA图片并利用glTexImage2D方法创建一个Texture,设置相应的过滤参数。简化版的TGA图片加载代码如下,下面的代码只保证加载非压缩的TGA图片,不支持采用RLE压缩的TGA图片:

// #pragma pack指定内存对齐方式
#pragma pack(push,x1)
// 这里表示按照1字节对齐方式对齐
#pragma pack(1)
// TGA图片格式,RLE算法
typedef struct {
    unsigned char  IdSize, // 图像信息字段长度, 范围 0 ~ 255, 0表示没有图像的信息字段
            MapType,        // 颜色表类型,0表示没有颜色。
            ImageType;      // 图像类型码,2表示非压缩格式,10表示压缩RGB格式,后面的图像数据采用RLE压缩

    unsigned short PaletteStart,    // 颜色表首地址,颜色表头的入口,整形(低位 —— 高位)
            PaletteSize;            // 颜色表的长度,整形(低位 —— 高位)
    unsigned char  PaletteEntryDepth;   // 颜色表的位数,16表示16位TGA,24表示24位TGA,32表示32位TGA

    unsigned short X,   // 图像X坐标起始位置,左下角的X坐标
            Y,          // 图像Y坐标起始位置,左下角的Y坐标
            Width,      // 图像的宽度
            Height;     // 图像的高度
    unsigned char  ColorDepth,  // 图像没像素存储占用位数
            Descriptor;         // 图像描述符字节

} TGA_HEADER;
#pragma pack(pop,x1)


/**
 * 加载一个 8bit,24bit或32bit TGA图片
 * @param context
 * @param fileName
 * @param width
 * @param height
 * @return
 */
char* loadTGA(void* context, const char *fileName, int *width, int *height) {
    char* buffer;
    AAsset* file;
    TGA_HEADER header;
    int bytes;
    file = fileOpen(context, fileName);
    if (file == nullptr) {
        ALOGE("fialed to load: {%s}\n", fileName);
        return nullptr;
    }

    bytes = fileRead(file, sizeof(TGA_HEADER), &header);
    *width = header.Width;
    *height = header.Height;
    //
    if (header.ColorDepth == 8 || header.ColorDepth == 24 || header.ColorDepth == 32) {
        int bytesToRead = sizeof(char) * (*width) * (*height) * header.ColorDepth / 8;

        buffer = (char *)malloc(bytesToRead);
        if (buffer) {
            bytes = fileRead(file, bytesToRead, buffer);
            fileClose(file);
            return buffer;
        }
    }

    return nullptr;
}

接下来是初始化部分,初始化主要完成加载glsl程序代码,创建program以及绑定统一变量,代码如下:

int init(ESContext *esContext) {
    UserData *userData = (UserData *) esContext->userData;

    char *vertexStr = readAssetFile("vertex_multitexture.glsl",
                                    esContext->activity->assetManager);
    char *fragmentStr = readAssetFile("fragment_multitexture.glsl",
                                      esContext->activity->assetManager);

    userData->programObject = loadProgram(vertexStr, fragmentStr);

    userData->baseMapLoc = glGetUniformLocation(userData->programObject, "s_baseMap");
    userData->lightMapLoc = glGetUniformLocation(userData->programObject, "s_lightMap");

    userData->baseMapTexId = loadTexture(esContext->activity->assetManager, "basemap.tga");
    userData->lightMapTexId = loadTexture(esContext->activity->assetManager, "lightmap.tga");

    if (userData->baseMapTexId == 0 || userData->lightMapTexId == 0) {
        return FALSE;
    }

    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    return TRUE;
}

完成初始化后,便是绘制部分,绘制部分主要完成使能Texture0 和 Texture1,然后使用glUniform1i方法绑定到对应的纹理等功能,代码如下:

void onDraw(ESContext *esContext) {
    UserData *userData = (UserData *) esContext->userData;
    // 顶点和texture
    GLfloat vertices[] = {
            -0.5f, 0.5f, 0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
            0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
            0.5f, 0.5f, 0.0f, 1.0f, 0.0f
    };
    // 索引
    GLushort indices[] = {0, 1, 2, 0, 2, 3};

    glViewport(0, 0, esContext->width, esContext->height);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(userData->programObject);
    glVertexAttribPointer(0, 3, GL_FLOAT,
                          GL_FALSE, 5 * sizeof(GLfloat), vertices);
    glVertexAttribPointer(1, 2, GL_FLOAT,
                          GL_FALSE, 5 * sizeof(GLfloat), &vertices[3]);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, userData->baseMapTexId);
    glUniform1i(userData->baseMapLoc, 0);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, userData->lightMapTexId);
    glUniform1i(userData->lightMapLoc, 1);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}

相关文章

  • 着色器

    顶点着色器 片段着色器 这个长方形所覆盖到的每一个像素,都会调用一次fragment shader。片段着色器的责...

  • (十)片段着色器

    本章描述片段着色器的更多细节。 固定功能片段着色器 在OpenGL ES 1.x版本有固定功能片段管线。这一小节主...

  • (十一)片段操作

    片段着色器的输出时片段的颜色和深度值。下图中的操作发生在片段着色器执行之后,可能影响像素的可见性和最终颜色。 缓冲...

  • OpenGL 几何着色器及billboard广告牌

    几何着色器 在顶点和片段着色器之间有一个可选的着色器叫几何着色器(Geometry Shader)。如果编译程序的...

  • OpenGL ES学习

    在 OpenGL ES 中你必须创建两种着色器:顶点着色器 (vertex shaders) 和片段着色器 (fr...

  • Hello Triangle

    使用OpenGL绘制图形的步骤 一般来说,顶点着色器和片段着色器需要我们自定义的。所以首先自定义顶点着色器和片段着...

  • 着色器-绚丽多彩的世界,从你开始!

    在Hello,OpenGL World文章中,我们并未过多的提及着色器,只是叫读者将顶点着色器、片段着色器的相关代...

  • OpenGL ES 3.0入门之顶点着色器和程序介绍

    原文地址: https://lm1024.xyz/archives/108 着色器 本文主要介绍顶点着色器和片段着...

  • Shader-基础

    简介 分类 固定管线着色器(没有嵌套CG语言 代码中没有CGPROGARAM的) 顶点shader(顶点片段着色器...

  • 高级OpenGL-02.模板测试(Stencil testing

    模板测试(Stencil testing) 当片段着色器处理完片段之后,模板测试(stencil test) 就开...

网友评论

    本文标题:片段着色器

    本文链接:https://www.haomeiwen.com/subject/lzywcxtx.html