Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用OpenGL直接使用屏幕空间坐标?_C#_Opengl_Glsl_Shader_Opentk - Fatal编程技术网

C# 如何使用OpenGL直接使用屏幕空间坐标?

C# 如何使用OpenGL直接使用屏幕空间坐标?,c#,opengl,glsl,shader,opentk,C#,Opengl,Glsl,Shader,Opentk,我已经按照我找到的指南在窗口上绘制了一个三角形,但整个过程对我来说似乎过于复杂了。在互联网上搜索,发现了许多类似的问题,但它们似乎都过时了,没有一个给出满意的答案 我已经阅读了上面提到的网站上的指南,其中有一张解释图形管道的图片,通过这张图片,我了解了为什么在屏幕上绘制三角形形状这样一项看似简单的任务需要这么多步骤 我也读过同一个网站的指南,它告诉我什么是OpenGL使用的“奇怪的(对我来说)坐标”,以及它为什么使用这个坐标 (这是上面提到的指南中的图片,我认为这对描述我的问题很有用。) 问

我已经按照我找到的指南在窗口上绘制了一个三角形,但整个过程对我来说似乎过于复杂了。在互联网上搜索,发现了许多类似的问题,但它们似乎都过时了,没有一个给出满意的答案

我已经阅读了上面提到的网站上的指南,其中有一张解释图形管道的图片,通过这张图片,我了解了为什么在屏幕上绘制三角形形状这样一项看似简单的任务需要这么多步骤

我也读过同一个网站的指南,它告诉我什么是OpenGL使用的“奇怪的(对我来说)坐标”,以及它为什么使用这个坐标

(这是上面提到的指南中的图片,我认为这对描述我的问题很有用。)


问题是:我可以直接使用最终的屏幕空间坐标吗? 我只想做一些2D渲染(没有z轴),屏幕大小是已知的(固定的),因此我看不出为什么我应该使用规范化的坐标系而不是绑定到屏幕上的特殊坐标系

例如:在320x240屏幕上,
(0,0)
表示左上角像素,
(319239)
表示右下角像素。它不需要和我描述的完全一样,它的想法是每个整数坐标=屏幕上对应的像素。

我知道可以设置这样一个坐标系供我自己使用,但坐标会在四周进行转换,最终返回到屏幕空间-,这是我首先拥有的。所有这些对我来说都是白费力气,当坐标变换时,会不会导致精度损失?


引自指南(上图):

  • 坐标在视图空间中之后,我们希望将它们投影到剪辑坐标剪辑坐标被处理为-1.0和1.0范围,并确定哪些顶点将最终出现在屏幕上
  • 考虑在1024x768屏幕上,我将剪辑坐标定义为(0,0)到(1024678),其中:

    假设我想在
    像素(11,11)
    处放置一张图片,因此该图片的剪辑坐标为
    剪辑(11.5,11.5)
    然后将该坐标处理为-1.0和1.0范围:

    11.5f * 2 / 1024 - 1.0f = -0.977539063f // x
    11.5f * 2 /  768 - 1.0f = -0.970052063f // y
    
    我有
    NDC(-0.977539063f,-0.970052063f)

  • 最后,我们将剪辑坐标转换为屏幕坐标,这一过程称为视口转换,将坐标从-1.0和1.0转换为glViewport定义的坐标范围。生成的坐标然后发送到光栅化器,将其转换为片段
  • 因此,获取NDC坐标并将其转换回屏幕坐标:

    (-0.977539063f + 1.0f) * 1024 / 2 = 11.5f        // exact
    (-0.970052063f + 1.0f) *  768 / 2 = 11.5000076f  // error
    
    x轴是精确的,因为1024是2的幂,但由于768不是如此,y轴是关闭的。误差很小,但不完全是11.5f,所以我猜会有某种混合,而不是原始图片的1:1表示


    为了避免上述舍入错误,我做了如下操作:

    float[] vertices = {
        //  x       y
          0.5f,   0.5f, 0.0f, // top-left
        319.5f,   0.5f, 0.0f, // top-right
        319.5f, 239.5f, 0.0f, // bottom-right
          0.5f, 239.5f, 0.0f, // bottom-left
    };
    
    首先,我将视口大小设置为大于窗口的大小,并将宽度和高度都设置为二的幂:

    GL.Viewport(0, 240 - 256, 512, 256); // Window Size is 320x240
    
    然后我设置顶点的坐标,如下所示:

    float[] vertices = {
        //  x       y
          0.5f,   0.5f, 0.0f, // top-left
        319.5f,   0.5f, 0.0f, // top-right
        319.5f, 239.5f, 0.0f, // bottom-right
          0.5f, 239.5f, 0.0f, // bottom-left
    };
    
    我在顶点着色器中手动转换它们:

    #version 330 core
    layout (location = 0) in vec3 aPos;
    
    void main()
    {
        gl_Position = vec4(aPos.x * 2 / 512 - 1.0, 0.0 - (aPos.y * 2 / 256 - 1.0), 0.0, 1.0);
    }
    
    最后我画了一个四边形,结果是:

    这似乎产生了一个正确的结果(四边形有一个320x240大小),但我想知道是否有必要做所有这些

  • 我的方法有什么缺点
  • 有没有更好的方法来实现我所做的

  • 线框模式下的渲染似乎隐藏了问题。我尝试将纹理应用到我的四边形上(实际上我切换到了2个三角形),在不同的GPU上得到了不同的结果,其中没有一个对我来说是正确的:

    左:英特尔HD4000 |右:英伟达GT635M(擎天柱)

    我将
    GL.ClearColor
    设置为白色并禁用纹理

    虽然这两个结果都填充了窗口客户端区域(320x240),但Intel给了我一个放在左上角的正方形319x239,而Nvidia给了我一个放在左下角的正方形319x239

    这是启用“纹理”后的外观

    纹理:

    (我将其垂直翻转,以便在代码中更轻松地加载)

    顶点:

    float[] vertices_with_texture = {
        //  x       y           texture x     texture y
          0.5f,   0.5f,        0.5f / 512, 511.5f / 512, // top-left
        319.5f,   0.5f,      319.5f / 512, 511.5f / 512, // top-right
        319.5f, 239.5f,      319.5f / 512, 271.5f / 512, // bottom-right ( 511.5f - height 240 = 271.5f)
          0.5f, 239.5f,        0.5f / 512, 271.5f / 512, // bottom-left
    };
    
    现在我完全被卡住了

    我以为我将四边形的边缘放置在精确的像素中心(.5)上,并在精确的像素中心(.5)上对纹理进行采样,但两张卡给了我两种不同的结果,其中没有一张是正确的(放大后,您可以看到图像的中心略微模糊,而不是清晰的棋盘格图案)

    我错过了什么


    我想我现在知道该怎么做了,我已经把解决方案作为答案发布了,并把这个问题留在这里供参考

    我可以直接使用最终屏幕空间坐标吗

    不,你不能。你必须变换坐标。在CPU或着色器(GPU)中

    如果要使用窗口坐标,则必须设置一个矩阵,将坐标从x:[-1.0,1.0],y:[-1.0,1.0](标准化设备空间)转换为窗口坐标x:[0320],y:[240,0]

    e、 g.通过使用

    glm::mat4投影=glm::orhto(0320.0f,240.0f,0,-1.0f,1.0f);
    
    e、 g.OpenTK

    OpenTK.Matrix4投影=
    OpenTK.Matrix4.CreateOrthographic(0320.0f,240.0f,0,-1.0f,1.0f);
    
    在顶点着色器中,顶点坐标必须与投影矩阵相乘

    在vec3顶点;
    均匀mat4投影;
    void main()
    {
    gl_位置=投影*vec4(顶点xyz,1.0);
    }
    

    为了完整起见,:
    (不要使用旧的和不推荐使用的传统OpenGL功能)

    格洛托(0.0320.0240.0,0.0,-1.0,1.0); 奥卡