OSG使用计算着色器

一、前言

尝试将在OpenGL下实现的FFT海洋移植到OSG内,用到了计算着色器,发现网上对于OSG使用计算着色器的资料很少,在此做个记录。

参考资料:杨石兴大佬的文章 https://blog.csdn.net/FreeSouthS/article/details/118971243 以及OSG源码中的osgcomputeshaders范例项目
基于OSG版本:3.6.3

二、代码

先直接上代码:

#include <osgViewer/viewer>
#include <osgDB/ReadFile>
#include <osg/Texture2D>
#include <osg/BindImageTexture>
#include <osg/DispatchCompute>
#include <osg/PolygonMode>

osg::Geometry* createFlat();

const int N = 16;

int main()
{
    
    
    float* Data1 = new float[N * N * 2];
    float* Data2 = new float[N * N * 2];
    for (int i = 0; i < N; i++) 
    {
    
    
        for (int j = 0; j < N; j++)
        {
    
    
            Data1[i * 2 * N + j * 2] = 0.5;
            Data1[i * 2 * N + j * 2 + 1] = 0.0;

            Data2[i * 2 * N + j * 2] = 0.0;
            Data2[i * 2 * N + j * 2 + 1] = 1.0;
        }
    }

    osg::ref_ptr<osg::Image> Data1lmg = new osg::Image;
    Data1lmg->setImage(N, N, 1, GL_RG32F, GL_RG, GL_FLOAT, (unsigned char*)Data1, osg::Image::USE_NEW_DELETE);
    osg::ref_ptr<osg::Texture2D> Data1Texture = new osg::Texture2D(Data1lmg);
    Data1Texture->setTextureSize(N, N);
    Data1Texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
    Data1Texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
    Data1Texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
    Data1Texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
    Data1Texture->setInternalFormat(GL_RG32F);
    Data1Texture->setSourceFormat(GL_RG);
    Data1Texture->setSourceType(GL_FLOAT);

    osg::ref_ptr<osg::Image> Data2lmg = new osg::Image;
    Data2lmg->setImage(N, N, 1, GL_RG32F, GL_RG, GL_FLOAT, (unsigned char*)Data2, osg::Image::USE_NEW_DELETE);
    osg::ref_ptr<osg::Texture2D> Data2Texture = new osg::Texture2D(Data2lmg);
    Data2Texture->setTextureSize(N, N);
    Data2Texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
    Data2Texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
    Data2Texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
    Data2Texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
    Data2Texture->setInternalFormat(GL_RG32F);
    Data2Texture->setSourceFormat(GL_RG);
    Data2Texture->setSourceType(GL_FLOAT);

    osg::ref_ptr<osg::Texture2D> DataTexture = new osg::Texture2D;
    DataTexture->setTextureSize(N, N);
    DataTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
    DataTexture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
    DataTexture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
    DataTexture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
    DataTexture->setInternalFormat(GL_RG32F);
    DataTexture->setSourceFormat(GL_RG);
    DataTexture->setSourceType(GL_FLOAT);

    osg::ref_ptr<osg::Texture2D> reTexture = new osg::Texture2D;
    reTexture->setTextureSize(N, N);
    reTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
    reTexture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
    reTexture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
    reTexture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
    reTexture->setInternalFormat(0x8814);//GL_RGBA32F
    reTexture->setSourceFormat(GL_RGBA);
    reTexture->setSourceType(GL_FLOAT);

    osg::ref_ptr<osg::BindImageTexture> Data1Binding = new osg::BindImageTexture(0, Data1Texture, osg::BindImageTexture::READ_ONLY, GL_RG32F);
    osg::ref_ptr<osg::BindImageTexture> Data2Binding = new osg::BindImageTexture(1, Data2Texture, osg::BindImageTexture::READ_ONLY, GL_RG32F);
    osg::ref_ptr<osg::BindImageTexture> DataBinding = new osg::BindImageTexture(2, DataTexture, osg::BindImageTexture::READ_WRITE, GL_RG32F);
    osg::ref_ptr<osg::BindImageTexture> reBinding = new osg::BindImageTexture(3, reTexture, osg::BindImageTexture::READ_WRITE, 0x8814);

    osg::ref_ptr<osg::Program> DataShader = new osg::Program;
    DataShader->addShader(osgDB::readRefShaderFile("./cs1.compute"));
    osg::ref_ptr<osg::Node> DataDispatchNode = new osg::DispatchCompute(1, 1, 1);
    DataDispatchNode->setDataVariance(osg::Object::DYNAMIC);
    DataDispatchNode->getOrCreateStateSet()->setAttributeAndModes(DataShader);
    DataDispatchNode->getOrCreateStateSet()->addUniform(new osg::Uniform("red_image", (int)0));
    //DataDispatchNode->getOrCreateStateSet()->setTextureAttributeAndModes(0, Data1Texture.get());
    DataDispatchNode->getOrCreateStateSet()->setAttributeAndModes(Data1Binding.get());
    DataDispatchNode->getOrCreateStateSet()->addUniform(new osg::Uniform("green_image", (int)1));
    //DataDispatchNode->getOrCreateStateSet()->setTextureAttributeAndModes(1, Data2Texture.get());
    DataDispatchNode->getOrCreateStateSet()->setAttributeAndModes(Data2Binding.get());
    DataDispatchNode->getOrCreateStateSet()->addUniform(new osg::Uniform("output_image", (int)2));
    //DataDispatchNode->getOrCreateStateSet()->setTextureAttributeAndModes(2, DataTexture.get());
    DataDispatchNode->getOrCreateStateSet()->setAttributeAndModes(DataBinding.get());

    osg::ref_ptr<osg::Program> reShader = new osg::Program;
    reShader->addShader(osgDB::readRefShaderFile("./cs2.compute"));
    osg::ref_ptr<osg::Node> reDispatchNode = new osg::DispatchCompute(1, 1, 1);
    reDispatchNode->setDataVariance(osg::Object::DYNAMIC);
    reDispatchNode->getOrCreateStateSet()->setAttributeAndModes(reShader);
    reDispatchNode->getOrCreateStateSet()->addUniform(new osg::Uniform("input_image", (int)2));
    //reDispatchNode->getOrCreateStateSet()->setTextureAttributeAndModes(0, DataTexture.get());
    reDispatchNode->getOrCreateStateSet()->setAttributeAndModes(DataBinding.get());
    reDispatchNode->getOrCreateStateSet()->addUniform(new osg::Uniform("output_image", (int)3));
    //reDispatchNode->getOrCreateStateSet()->setTextureAttributeAndModes(1, reTexture.get());
    reDispatchNode->getOrCreateStateSet()->setAttributeAndModes(reBinding.get());

    auto quad = createFlat();
    quad->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

    //use Shader
    osg::ref_ptr<osg::StateSet> ss = quad->getOrCreateStateSet();
    osg::ref_ptr<osg::Program> program = new osg::Program;
    osg::ref_ptr<osg::Shader> vertShader = new osg::Shader(osg::Shader::VERTEX);
    osg::ref_ptr<osg::Shader> fragShader = new osg::Shader(osg::Shader::FRAGMENT);
    vertShader->loadShaderSourceFromFile("./Test.vert");
    fragShader->loadShaderSourceFromFile("./Test.frag");
    program->addShader(vertShader);
    program->addShader(fragShader);
    ss->setAttributeAndModes(program);
    ss->setTextureAttributeAndModes(0, reTexture.get());
    ss->setAttributeAndModes(reBinding.get());

    osg::ref_ptr<osg::Group> scene = new osg::Group;
    scene->addChild(DataDispatchNode);
    scene->addChild(reDispatchNode);
    scene->addChild(quad);

    osgViewer::Viewer viewer;
    viewer.setSceneData(scene);
    viewer.realize();
    //viewer.getCamera()->getGraphicsContext()->getState()->resetVertexAttributeAlias(false);
    viewer.getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true);
    viewer.getCamera()->getGraphicsContext()->getState()->setUseVertexAttributeAliasing(true);
    return viewer.run();
}

osg::Geometry* createFlat() 
{
    
    
    osg::Geometry* geom = new osg::Geometry;
    geom->setUseVertexBufferObjects(true);

    osg::Vec3Array* vArr = new osg::Vec3Array;
    osg::Vec2Array* tArr = new osg::Vec2Array;

    vArr->push_back(osg::Vec3(1.0, 1.0, 0.0));
    vArr->push_back(osg::Vec3(1.0, -1.0, 0.0));
    vArr->push_back(osg::Vec3(-1.0, -1.0, 0.0));
    vArr->push_back(osg::Vec3(-1.0, 1.0, 0.0));

    tArr->push_back(osg::Vec2(0.0, 0.0));
    tArr->push_back(osg::Vec2(1.0, 0.0));
    tArr->push_back(osg::Vec2(1.0, 1.0));
    tArr->push_back(osg::Vec2(0.0, 1.0));

    osg::DrawElementsUInt* el = new osg::DrawElementsUInt(GL_QUADS);

    el->push_back(0);
    el->push_back(1);
    el->push_back(2);
    el->push_back(3);

    geom->setVertexArray(vArr);

    //geom->setTexCoordArray(0, tArr);
    geom->setVertexAttribArray(1, tArr, osg::Array::BIND_PER_VERTEX);

    geom->addPrimitiveSet(el);

    return geom;
}

计算着色器1:

#version 430

layout (local_size_x = 16, local_size_y = 16) in;

layout (binding = 0, rg32f) uniform image2D red_image;
layout (binding = 1, rg32f) uniform image2D green_image;
layout (binding = 2, rg32f) uniform image2D output_image;

void main()
{
    
    
	vec2 v1 = imageLoad(red_image, ivec2(gl_GlobalInvocationID.xy)).xy;
	vec2 v2 = imageLoad(green_image, ivec2(gl_GlobalInvocationID.xy)).xy;
	vec2 v3 = v1 + v2;

	imageStore(output_image, ivec2(gl_GlobalInvocationID.xy), vec4(v3, 0.0, 1.0));
}

计算着色器2:

#version 430

layout (local_size_x = 16, local_size_y = 16) in;

layout (binding = 0, rg32f) uniform image2D input_image;
layout (binding = 1, rgba32f) uniform image2D output_image;

void main()
{
    
    
	vec2 v1 = imageLoad(input_image, ivec2(gl_GlobalInvocationID.xy)).xy;

	imageStore(output_image, ivec2(gl_GlobalInvocationID.xy), vec4(v1, 1.0, 1.0));
}

顶点着色器:

#version 430

layout(binding = 0) uniform sampler2D testTexture;

layout(location = 0) in vec3 vPos;
layout(location = 1) in vec2 tPos;

uniform mat4 osg_ModelViewProjectionMatrix;

out vec3 color;

void main()
{
    
    
	color = texture(testTexture, tPos).rgb;

	gl_Position = osg_ModelViewProjectionMatrix*vec4(vPos,1.0);
}

片元着色器:

#version 430

in vec3 color;

out vec4 FragColor;

void main()
{
    
    
	FragColor = vec4(color, 1.0);
}

结果:

在这里插入图片描述

代码功能就是生成一张每个像素0.5红色的材质,生成一张每个像素绿色的材质,在计算着色器1里将每个像素两个颜色相加后输出,然后输入进计算着色器2中将每个像素加上蓝色后输出,然后将材质赋予一个矩形平面。

三、记录

osg::ref_ptr<osg::BindImageTexture> Data1Binding = new osg::BindImageTexture(0, Data1Texture, osg::BindImageTexture::READ_ONLY, GL_RG32F);
//三个函数
DataDispatchNode->getOrCreateStateSet()->addUniform(new osg::Uniform("red_image", (int)0));//1
DataDispatchNode->getOrCreateStateSet()->setTextureAttributeAndModes(0, Data1Texture.get());//2
DataDispatchNode->getOrCreateStateSet()->setAttributeAndModes(Data1Binding.get());//3

0、 最主要的就是上面这个类的对象和这三个函数的调用。
1、 BindImageTexture似乎跟GL_TEXTURE0、GL_TEXTURE1、GL_TEXTURE2等等相关,相当于将材质绑定到了对应的GL_TEXTURE上。
2、 addUniform(new osg::Uniform(“red_image”, (int)0));对于给Shader中的贴图uniform设置这个,即是将对应序号的GL_TEXTURE绑定到这个uniform上。(这个跟OpenGL中的用法一致)
3、 似乎读数据的话必须要调用setAttributeAndModes(BindImageTexture*)。(因为这个卡了很久,先是参考的osgcomputeshaders范例项目,里面计算着色器并没有读取贴图数据,所以没有调用这个,导致我自己代码的贴图数据读不出来,死活找不到原因,后来还是偶然发现了大佬的文章,里面范例代码的计算着色器虽然也没有读取贴图数据,但是加上了这个调用,就奇怪有什么区别,试了一下发现问题解决了)
4、 在创建BindImageTexture对象后,如果不调用上面三个函数,计算着色器则会默认绑定对应创建时绑定的贴图对象。
5、 顶点着色器中读取sampler2D时必须调用setTextureAttributeAndModes(0, Data1Texture.get());和setAttributeAndModes(Data1Binding.get());这两个函数,跟设置uniform无关。setTextureAttributeAndModes似乎是跟location(binding)设置相关。
6、 对于计算着色器的设置,setTextureAttributeAndModes似乎无效,主要以addUniform设置为准。

小结: 对于计算着色器主要设置addUniform,对于顶点片元着色器主要设置setTextureAttributeAndModes,如果需要读取数据则需要调用setAttributeAndModes(BindImageTexture*),BindImageTexture对象则相当于绑定到对应的GL_TEXTURE位置上。

猜你喜欢

转载自blog.csdn.net/qq_45523399/article/details/128178481