C# 渲染 OpenCL 生成的图像 [英] C# Rendering OpenCL-generated image

查看:24
本文介绍了C# 渲染 OpenCL 生成的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:我正在尝试实时渲染动态 Julia 分形.因为分形在不断变化,我需要能够每秒渲染至少 20 帧,最好更多.关于 Julia 分形,您需要了解的是,每个像素都可以独立计算,因此该任务很容易并行化.

Problem: I'm trying to render a dynamic Julia fractal in real time. Because the fractal is constantly changing, I need to be able to render at least 20 frames per second, preferably more. What you need to know about a Julia fractal is that every pixel can be calculated independently, so the task is easy parallelizable.

第一种方法:因为我已经习惯了 C# 中的 Monogame,所以我尝试在 HLSL 中编写一个可以完成这项工作的着色器,但是编译器一直抱怨,因为我使用了超过允许的数量64 个算术槽(我至少需要一千个).

First approach: Because I'm already used to Monogame in C#, I tried writing a shader in HLSL that would do the job, but the compiler kept complaining because I used up more than the allowable 64 arithmetic slots (I need at least a thousand).

第二种方法:使用 CPU,正如预期的那样,生成一帧大约需要两分钟.

Second approach: Using the CPU, it took, as could be expected, about two minutes to generate one frame.

第三种方法:我开始使用名为 Cloo 的包装器学习 OpenCL 的基础知识.通过使用 OpenCL 计算图像数据,然后从 GPU 获取数据,将数据存储在 Texture2D 中并将纹理绘制到屏幕上,我实际上得到了一个快速、漂亮的结果.对于 1000x1000 的图像,我每秒得到大约 13 帧.这仍然不是我所希望的,因为图像应该是 1920x1080 来填满我的屏幕,而且帧速率非常明显.我意识到我实际上是在 GPU 上生成图像,将数据发送到 CPU,然后将其发送回 GPU,所以这似乎是一个不必要的步骤,如果可以删除,可能会解决我的问题.我在一些论坛上读到 OpenGL 能够做到这一点,但我无法找到具体信息.

Third approach: I started learning the basics of OpenCL using a wrapper called Cloo. I actually got a quick, nice result by calculating the image data using OpenCL, then getting the data from the GPU, storing the data in a Texture2D and drawing the texture to the screen. For a 1000x1000 image I get about 13 frames a second. This is still not quite what I had hoped for, as the image should be 1920x1080 to fill up my screen, and the frame rate is pretty noticeable. I realised that I'm actually generating the image on the GPU, sending the data to the CPU and then sending it back to the GPU, so this seems like an unnecessary step that, if could be removed, will probably solve my problem. I read on some fora that OpenGL is able to do this, but I haven't been able to find specific information.

问题: 首先,有没有一种简单的方法可以直接绘制OpenCL生成的数据而不涉及CPU(最好兼容Monogame)?如果不是这种情况,是否可以使用 OpenGL 实现它,然后将它与 Monogame 结合起来?其次,为什么这不能用简单的 HLSL 着色器实现?由于 HLSL 和 OpenCL 都使用 GPU,为什么 HLSL 在执行许多算术运算时会受到如此多的限制?

Questions: Firstly, is there a simple way to draw the data generated by OpenCL directly without involving CPU (preferably compatible with Monogame)? If this isn't the case, is it possible to implement it using OpenGL and afterwards combine it with Monogame? Secondly, why isn't this possible with a simple HLSL shader? As HLSL and OpenCL both use the GPU, why is HLSL so much more limited when it comes to doing many arithmetic operations?

我发现 this 网站大致可以满足我的要求,但使用的是 GLSL 着色器.这再次质疑我在 HLSL 中的成见.不幸的是,由于 monogame 不支持 GLSL(目前),我的问题仍未得到解答.

I found this site that does roughly what I want, but using a GLSL shader. This again questions my fait in HLSL. Unfortunately, as monogame doesn't support GLSL (yet), my questions remain unanswered.

推荐答案

抱歉,我不使用 OpenCLC# 但您可以使用 在着色器中完全执行此操作>GLSL(但是对于 Julia,您可能会遇到精度问题,例如分形有时甚至 64 位 double 还不够).无论如何,这是我几年前做的 Mandelbrot 集的一个简单示例...

Sorry I do not use OpenCL nor C# but You can do this fully inside shaders using GLSL (but you might have precision problems as for Julia like fractals is sometimes even 64bit double not enough). Anyway here a simple example of Mandelbrot set I did some years back...

CPU 端应用 C++/OpenGL/GLSL/VCL 代码::

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h" // VCL window header
#include "gl\OpenGL3D_double.cpp" // my GL engine
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
OpenGLscreen scr;
GLSLprogram shd;
float mx=0.0,my=0.0,mx0=0.0,my0=0.0,mx1=0.0,my1=0.0;
TShiftState sh0,sh1;
int xs=1,ys=1;
int txrmap=-1;
float zoom=1.000;
unsigned int queryID[2];
//---------------------------------------------------------------------------
void gl_draw()
    {
    float x,y,dx,dy;
    scr.cls();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // matrix for old GL rendering
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();


    // GLSL uniforms
    shd.bind();
    shd.set1i("txrmap",0);      // texture unit
    shd.set2f("p0",mx,my);      // pan position
    shd.set1f("zoom",zoom);     // zoom

    // issue the first query
    // Records the time only after all previous
    // commands have been completed
    glQueryCounter(queryID[0], GL_TIMESTAMP);

    // QUAD covering screen
    scr.txrs.bind(txrmap);
    glColor3f(1.0,1.0,1.0);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0,0.0); glVertex2f(-1.0,+1.0);
    glTexCoord2f(0.0,1.0); glVertex2f(-1.0,-1.0);
    glTexCoord2f(1.0,1.0); glVertex2f(+1.0,-1.0);
    glTexCoord2f(1.0,0.0); glVertex2f(+1.0,+1.0);
    glEnd();
    shd.unbind();
    scr.txrs.unbind();

    // issue the second query
    // records the time when the sequence of OpenGL
    // commands has been fully executed
    glQueryCounter(queryID[1], GL_TIMESTAMP);


    // GL driver info and GLSL log
    scr.text_init_pix(1.0);
    glColor4f(0.0,0.2,1.0,0.8);
    scr.text(glGetAnsiString(GL_VENDOR));
    scr.text(glGetAnsiString(GL_RENDERER));
    scr.text("OpenGL ver: "+glGetAnsiString(GL_VERSION));
    glColor4f(0.4,0.7,0.8,0.8);
    for (int i=1;i<=shd.log.Length();) scr.text(str_load_lin(shd.log,i,true));
    scr.text_exit();

    scr.exe();
    scr.rfs();

    // wait until the results are available
    int e;
    unsigned __int64 t0,t1;
    for (e=0;!e;) glGetQueryObjectiv(queryID[0],GL_QUERY_RESULT_AVAILABLE,&e);
    for (e=0;!e;) glGetQueryObjectiv(queryID[1],GL_QUERY_RESULT_AVAILABLE,&e);
    glGetQueryObjectui64v(queryID[0], GL_QUERY_RESULT, &t0);
    glGetQueryObjectui64v(queryID[1], GL_QUERY_RESULT, &t1);
    Form1->Caption=AnsiString().sprintf("Time spent on the GPU: %f ms
", (t1-t0)/1000000.0);
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    scr.init(this);

    OpenGLtexture txr;
    txr.load      ("gradient.jpg");
    txrmap=scr.txrs.add(txr);

    shd.set_source_file("","","","Mandelbrot_set.glsl_vert","Mandelbrot_set.glsl_frag");

    glGenQueries(2, queryID);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
    scr.exit();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
    {
    scr.resize();
    xs=ClientWidth;
    ys=ClientHeight;
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
    {
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X,int Y)
    {
    bool q0,q1;
    mx1=1.0-divide(X+X,xs-1);
    my1=divide(Y+Y,ys-1)-1.0;
    sh1=Shift;
    q0=sh0.Contains(ssLeft);
    q1=sh1.Contains(ssLeft);
    if (q1)
        {
        mx-=(mx1-mx0)*zoom;
        my-=(my1-my0)*zoom;
        }
    mx0=mx1; my0=my1; sh0=sh1;
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y)
    {
    FormMouseMove(Sender,Shift,X,Y);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y)
    {
    FormMouseMove(Sender,Shift,X,Y);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelDown(TObject *Sender, TShiftState Shift, TPoint &MousePos, bool &Handled)
    {
    zoom*=1.2;
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelUp(TObject *Sender, TShiftState Shift, TPoint &MousePos, bool &Handled)
    {
    zoom/=1.2;
    gl_draw();
    }
//---------------------------------------------------------------------------

你可以忽略大部分代码,重要的是 gl_draw() 渲染单个 QUAD 覆盖整个屏幕并传递 zoom>平移位置.此代码使用旧式 glBegin/glEnd 和默认的 nVidia 位置,因此它可能无法在不同供应商的 gfx 驱动程序上运行.网格应该在 VAO/VBO 中,因此布局位置将匹配以了解如何操作,请查看答案末尾的链接或将着色器移植到兼容性配置文件中.

You can ignore most of the code the important stuff is gl_draw() rendering single QUAD covering whole screen and passing zoom and pan position. This code uses old style glBegin/glEnd and default nVidia locations so it may not work on different vendor gfx drivers. The mesh should be in VAO/VBO so the layout locations will match to see how to do it take a look at the link on the end of answer or port the shaders to compatibility profile.

顶点:

// Vertex
#version 420 core
layout(location=0) in vec2 pos;     // glVertex2f <-1,+1>
out smooth vec2 p;                  // texture end point <0,1>
void main()
    {
    p=pos;
    gl_Position=vec4(pos,0.0,1.0);
    }

片段:

// Fragment
#version 420 core
uniform sampler2D txrmap;           // texture unit for light map
uniform vec2 p0=vec2(0.0,0.0);      // mouse position <-1,+1>
uniform float zoom=1.000;           // zoom [-]
in smooth vec2 p;
out vec4 col;
void main()
    {
    int i,n;
    vec2 pp;
    float x,y,q,xx,yy;
    pp=(p*zoom)-p0;         // y (-1, 1)
    pp.x=(1.75*pp.x)-0.75;  // x (-2.5, 1)
    for (x=0.0,y=0.0,xx=0.0,yy=0.0,i=0,n=200;(i<n)&&(xx+yy<4.0);i++)
        {
        q=xx-yy+pp.x;
        y=(2.0*x*y)+pp.y;
        x=q;
        xx=x*x;
        yy=y*y;
        }
    q=float(i)/float(n);
    col=texture2D(txrmap,vec2(q,0.5));
//  col=vec4(q,q,q,1.0);
    }

使用此纹理作为渐变:

这里的结果截图:

如果您需要开始使用 GLSL(替换我的 gl 引擎内容),请参阅:

In case you need to get started with GLSL (to replace my gl engine stuff) see:

但我确信 C# 中肯定有大量的相关教程,所以 google

but I am sure there must be tons of tutorials for this in C# so google

如果您对色彩增强感兴趣,请参阅:

If you are interested in color enhancing see:

这篇关于C# 渲染 OpenCL 生成的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆