OpenGL 点精灵效果

点精灵效果简介

点精灵其实是OpenGL 1.5及之后的版本所支持的一个特性,通过点精灵这个特性,我们可以通过绘制1个3D点,去将一个2D纹理图像显示在屏幕的任意位置。所以我们可以用点去表示屏幕上移动的大量的粒子,来产生一些视觉效果。把这些点表示为重叠2D图像是可以做成动画细丝的效果的。其实OpenGL一直都是支持对点进行纹理贴图的,但是在1.5版本之前,这意味着只是将单个纹理坐标应用于整个点。较大的经过纹理贴图的点就是经过过滤的单个纹理单元的放大版本,所以肯定是没有点精灵效果高的。

比如说下面的这种Mac下的一种屏幕保护效果
这里写图片描述

如果在点精灵效果出来之前,要实现这样的效果,需要在屏幕上绘制大量的纹理四边形或者是三角形带(额,这块地方可以理解,但是毕竟没经历过那种时代),然后这个屏幕保护效果是会旋转的,所以我们还需要对每个多边形执行开销巨大的旋转来实现,因为我们要确保它能面对着照相机,或者说在2D正交投影下绘制所有的粒子,因为如果超出正交投影所设置的边界是不会进行绘制的,点精灵效果是允许我们发送单个3D顶点,以此来渲染一个完美对齐的纹理2D多边形,点精灵所需要的带宽只有四边形发送4个顶点所需带宽的四分之一

关于点精灵的使用,我们需要做的就是去简单地绑定一个2D纹理,需要注意的是我们不能同时的去使用点精灵和抗锯齿点。因为点精灵已经是默认的点光栅化模式了,如果开启了点平滑就不是处于这个模式了(目前也在摸索)。

我们在着色器程序中有一个内置只读变量就是gl_PointCoord,它的值是当前片元着所在点图元的二维坐标点,如果当前的图元不是一个点,那么这个值是读出来是未被定义的。
所以关于片元着色器的内容,我们可以这么写

#version 120

//传入的颜色值
varying vec4 vStarColor;

//采样器
uniform sampler2D  starImage;


void main(void)
{
    //表示当前所在点图元的二维坐标点
    gl_FragColor = texture2D(starImage, gl_PointCoord) * vStarColor;
}

我们要想在顶点着色器中去确定点最终光栅化的大小,我们可以去设置一个内置变量gl_PointSize,但是在此之前,我们需要去做的就是去启动点大小的模式

glEnable(GL_PROGRAM_POINT_SIZE);

接下来我们来看下顶点着色器的内容,这里其实做的就是将点的颜色传递给片元着色器,以及去设置粒子的z位置根据时间间隔,以及去设置粒子的大小根据点的z值

//传入顶点
attribute vec4 vVertex;
//传入颜色
attribute vec4 vColor;

//传入模型视图投影矩阵
uniform mat4   mvpMatrix;

//传入时间值
uniform float  timeStamp;

//传入颜色
varying vec4 vStarColor;

void main(void)
{
    //传递顶点
    vec4 vNewVertex = vVertex;

    //设置颜色
    vStarColor = vColor;

    // Offset by running time, makes it move closer
    //设置新的位置的z值要加上timeStamp
    vNewVertex.z += timeStamp;

    // If out of range, adjust
    //如果z值到达临近裁剪面的时候,只要将它们的位置进行循环回到远裁剪面就可以了
    if(vNewVertex.z > -1.0)
        vNewVertex.z -= 999.0;

    //设置点的大小,使用平方根也就是说z值越大,粒子越大,因为vNewVertex都为负数
    gl_PointSize = 30.0 + (vNewVertex.z / sqrt(-vNewVertex.z));

    // If they are very small, fade them up
    //判断如果粒子太小了,就会发生闪烁,所以我们要去使这个点的颜色逐渐变暗,从而使它们在视野中消失。
    if(gl_PointSize < 4.0)
        vStarColor = smoothstep(0.0, 4.0, gl_PointSize) * vStarColor;


    // Don't forget to transform the geometry! 设置点的位置
    gl_Position = mvpMatrix * vNewVertex;
}

其他的代码如下所示

#include "GLShaderManager.h"

#include <GLTools.h>

#include <glut/glut.h>

#include <GLFrustum.h>
#include <StopWatch.h>

#include <math.h>
#include <stdlib.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#define NUM_STARS 10000

//传入透视投影矩阵 Frustum:截头锥体;平截头体
GLFrustum           viewFrustum;

//批次类容器 Batch:批处理
GLBatch             starsBatch;

//粒子效果着色器程序 StarField 飞舞的星星 星空 粒子效果
GLuint    starFieldShader;    // The point sprite shader

//MVP矩阵
GLint    locMVP;                // The location of the ModelViewProjection matrix uniform

//时间戳
GLint   locTimeStamp;       // The location of the time stamp

//纹理的uniform值
GLint    locTexture;            // The location of the  texture uniform

//星星纹理
GLuint    starTexture;        // The star texture texture object


//加载TGA文件为纹理的方法
// Load a TGA as a 2D Texture. Completely initialize the state

bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
    GLbyte *pBits;
    int nWidth, nHeight, nComponents;
    GLenum eFormat;

    // Read the texture bits
    //读取TGA文件来获得指针的的内存指向
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);

    //如果pBits==NULL就返回false
    if(pBits == NULL)
        return false;

    //设置环绕模式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);

    //设置过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);

    //指定OpenGL 如何从数据缓冲区中解包图像数据
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    //加载纹理,将纹理载入内存,一旦被载入之后这些纹理就会变成当前纹理状态
    glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,
                 eFormat, GL_UNSIGNED_BYTE, pBits);

    //释放指针所指向的那块数据缓冲区
    free(pBits);

    //指定过滤方式
    if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
       minFilter == GL_LINEAR_MIPMAP_NEAREST ||
       minFilter == GL_NEAREST_MIPMAP_LINEAR ||
       minFilter == GL_NEAREST_MIPMAP_NEAREST)
    {
        //生成map贴图
        glGenerateMipmap(GL_TEXTURE_2D);

    }

    return true;
}

void SetupRC(void)
{

    //设置清屏颜色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f );

    //开启点精灵模式 启用点块纹理
    glEnable(GL_POINT_SPRITE);

    //设置四种颜色
    GLfloat fColors[4][4] = {{ 1.0f, 1.0f, 1.0f, 1.0f}, // White
        { 0.67f, 0.68f, 0.82f, 1.0f}, // Blue Stars
        { 1.0f, 0.5f, 0.5f, 1.0f}, // Reddish
        { 1.0f, 0.82f, 0.65f, 1.0f}}; // Orange


    // Randomly place the stars in their initial positions, and pick a random color 随机地将星星放置在它们的初始位置,然后选择一个随机颜色
    starsBatch.Begin(GL_POINTS, NUM_STARS);

    for(int i = 0; i < NUM_STARS; i++)
    {
        //随机产生颜色
        int iColor = 0;        // All stars start as white

        // One in five will be blue
        if(rand() % 5 == 1)
            iColor = 1;

        // One in 50 red
        if(rand() % 50 == 1)
            iColor = 2;

        // One in 100 is amber
        if(rand() % 100 == 1)
            iColor = 3;


        starsBatch.Color4fv(fColors[iColor]);

        //随机产生位置
        M3DVector3f vPosition;
        vPosition[0] = float(3000 - (rand() % 6000)) * 0.1f;
        vPosition[1] = float(3000 - (rand() % 6000)) * 0.1f;
        vPosition[2] = -float(rand() % 1000)-1.0f;  // -1 to -1000.0f

        //三角形批次类拷贝顶点
        starsBatch.Vertex3fv(vPosition);
    }

    //结束存放数据
    starsBatch.End();

    //加载着色器程序
    starFieldShader = gltLoadShaderPairWithAttributes("SpaceFlight.vsh", "SpaceFlight.fsh", 2, GLT_ATTRIBUTE_VERTEX, "vVertex",
                                                      GLT_ATTRIBUTE_COLOR, "vColor");

    //获取模型视图矩阵的id
    locMVP = glGetUniformLocation(starFieldShader, "mvpMatrix");
    //设置纹理采样器
    locTexture = glGetUniformLocation(starFieldShader, "starImage");
    //timeStamp为时间传入
    locTimeStamp = glGetUniformLocation(starFieldShader, "timeStamp");

    //生成纹理对象
    glGenTextures(1, &starTexture);
    //绑定纹理
    glBindTexture(GL_TEXTURE_2D, starTexture);

    //加载纹理,影响的是绑定的纹理对象
    LoadTGATexture("Star.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
}

// Cleanup
void ShutdownRC(void)
{
    //删除纹理对象
    glDeleteTextures(1, &starTexture);
}


// Called to draw scene
void RenderScene(void)
{
    static CStopWatch timer;

    // Clear the window and the depth buffer
    //清除屏幕缓冲区
    glClear(GL_COLOR_BUFFER_BIT);

    //开启混合
    // Turn on additive blending
    glEnable(GL_BLEND);

    //由于纹理的背景颜色也为黑色,所以直接颜色进行相加
    glBlendFunc(GL_ONE, GL_ONE);


    // Let the vertex program determine the point size 让顶点程序确定点大小
    glEnable(GL_PROGRAM_POINT_SIZE);

    // Bind to our shader, set uniforms 使用着色器程序
    glUseProgram(starFieldShader);

    //直接用投影矩阵传入模型视图投影矩阵
    glUniformMatrix4fv(locMVP, 1, GL_FALSE, viewFrustum.GetProjectionMatrix());
    //绑定纹理单元
    glUniform1i(locTexture, 0);


    //获取时间间隔
    // fTime goes from 0.0 to 999.0 and recycles
    float fTime = timer.GetElapsedSeconds() * 10.0f;

    //fTime进行取余
    fTime = fmod(fTime, 999.0f);

    //传入fTime
    glUniform1f(locTimeStamp, fTime);

    // Draw the stars 开始绘制
    starsBatch.Draw();

    //交换双缓冲区
    glutSwapBuffers();

    //标记为重新绘制
    glutPostRedisplay();
}



void ChangeSize(int w, int h)
{
    // Prevent a divide by zero
    if(h == 0)
        h = 1;

    //设置视口
    // Set Viewport to window dimensions
    glViewport(0, 0, w, h);

    //设置投影视图
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 1000.0f);
}

///
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Spaced Out");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }

    SetupRC();
    glutMainLoop();
    ShutdownRC();
    return 0;
}

star.tga文件是这样的,因为它的背景是黑色的,所以我们只需要去做颜色相加来混合就可以了
这里写图片描述

粒子效果如下所示
这里写图片描述

我们可以利用gl_PointCoord和discard来完成丢弃位于我们想要的点形状范围之外的片段,我们先看看先去修改清屏颜色和关闭混合效果得到的效果是什么

这里写图片描述

我们修改片段着色器

#version 120

//传入的颜色值
varying vec4 vStarColor;

//采样器
uniform sampler2D  starImage;

void main(void)
{

    vec2 p = gl_PointCoord *2.0 -vec2(1.0);

    if(dot(p,p)>1.0)
        discard;

    //表示当前所在点图元的二维坐标点
    gl_FragColor = texture2D(starImage, gl_PointCoord) * vStarColor;
}

我们可以看到原本是正方形的点变成了圆形
这里写图片描述

生成花朵图形,可以将着色器程序改为下面的代码

#version 120

//传入的颜色值
varying vec4 vStarColor;

//采样器
uniform sampler2D  starImage;

void main(void)
{

    vec2 temp = gl_PointCoord *2.0 - vec2(1.0);

    //atan可返回数字的反正切值
    if(dot(temp,temp)>sin(atan(temp.y,temp.x)*0.5))
        discard;

    //表示当前所在点图元的二维坐标点
    gl_FragColor = texture2D(starImage, gl_PointCoord) * vStarColor;
}

效果如下所示
这里写图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值