从像素到NDC的转换 [英] Transformations from pixels to NDC

查看:174
本文介绍了从像素到NDC的转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我的屏幕为(800 * 600),并且使用 Triangle_Strip (在NDC中)用以下顶点位置绘制了四边形(2D):

Let's say that my screen is (800 * 600) and i have a Quad (2D) drawn with the following vertices positions using Triangle_Strip (in NDC) :

float[] vertices = {-0.2f,0.2f,-0.2f,-0.2f,0.2f,0.2f,0.2f,-0.2f};

然后我以这种方式设置我的转换矩阵:

And I set up my Transformation Matrix in this way :

Vector2f position = new Vector2f(0,0);
Vector2f size = new Vector2f(1.0f,1.0f);

Matrix4f tranMatrix = new Matrix4f();
tranMatrix.setIdentity();
Matrix4f.translate(position, tranMatrix, tranMatrix);
Matrix4f.scale(new Vector3f(size.x, size.y, 1f), tranMatrix, tranMatrix);

还有我的顶点着色器:

#version 150 core

in vec2 in_Position;

uniform mat4 transMatrix;

void main(void) {

gl_Position = transMatrix * vec4(in_Position,0,1.0);

}

我的问题是,我应该使用哪个公式来修改四边形的坐标转换(以像素为单位)?

My question is, which formula should I use to modify the transformations of my quad with coordinates (in Pixels) ?

例如:

-设置比例 :( 50像素,50像素)=> Vector2f(width,height)

-set Scale : (50px, 50px) => Vector2f(width,height)

-设置位置:(100px,100px)=> Vector2f(x,y)

-set Position : (100px, 100px) => Vector2f(x,y)

为了更好地理解,我将创建一个函数将我的Pixels数据转换为NDC,以将其发送到顶点着色器旁边.建议我使用正交投影,但是我不知道如何正确地创建它可以在我的顶点着色器中看到,我不使用任何投影矩阵.谢谢!

To better understand, I would create a function to convert my Pixels data to NDCs to send them next to the vertex shader.I was advised to use an Orthographic projection but I don't know how to create it correctly and as you can see in my vertex shader I don't use any projection matrix. Thank you !

这是一个与我的话题相似但不太明确的话题:

Here is a topic similar to mine but not very clear :

-转换为NDC,计算并转换回世界空间

我按照以下公式创建了正交投影矩阵,但是 似乎什么也没出现,这是我的操作方法:

I created my orthographic projection matrix by following the formula but nothing seems to appear, here is how I proceeded :

public static Matrix4f glOrtho(float left, float right, float bottom, float top, float near, float far){

    final Matrix4f matrix = new Matrix4f();
    matrix.setIdentity();

    matrix.m00 = 2.0f / (right - left);
    matrix.m01 = 0;
    matrix.m02 = 0;
    matrix.m03 = 0;

    matrix.m10 = 0;
    matrix.m11 = 2.0f / (top - bottom);
    matrix.m12 = 0;
    matrix.m13 = 0;

    matrix.m20 = 0;
    matrix.m21 = 0;
    matrix.m22 = -2.0f / (far - near);
    matrix.m23 = 0;

    matrix.m30 = -(right+left)/(right-left);
    matrix.m31 = -(top+bottom)/(top-bottom);
    matrix.m32 = -(far+near)/(far-near);
    matrix.m33 = 1;

    return matrix;
}

然后我将矩阵包含在顶点着色器中

I then included my matrix in the vertex shader

#version 140

in vec2 position;

uniform mat4 projMatrix;

void main(void){

gl_Position = projMatrix * vec4(position,0.0,1.0);

}

我想念什么?

推荐答案

新答案

在注释中进行了澄清之后,所提出的问题可以总结为:

New Answer

After clarifications in the comments, the question being asked can be summed up as:

如何有效地将四边形转换为可在GUI中使用的像素?

How do I effectively transform a quad in terms of pixels for use in a GUI?

如原始问题中所述,最简单的方法是使用正交投影.什么是正交投影?

As mentioned in the original question, the simplest approach to this will be using an Orthographic Projection. What is an Orthographic Projection?

一种投影方法,其中描绘对象或使用平行线绘制表面以将其形状投影到平面上.

a method of projection in which an object is depicted or a surface mapped using parallel lines to project its shape onto a plane.

在实践中,您可能会将此视为2D投影.距离不起作用,并且OpenGL坐标映射到像素坐标. 请参阅此答案以获取更多信息.

In practice, you may think of this as a 2D projection. Distance plays no role, and the OpenGL coordinates map to pixel coordinates. See this answer for a bit more information.

通过使用正交投影而不是透视投影,您可以开始考虑所有像素转换.

By using an Orthographic Projection instead of a Perspective Projection you can start thinking of all of your transformations in terms of pixels.

不是将四边形定义为维度上的(25 x 25)个世界单位,而是维度上的(25 x 25)个像素.

Instead of defining a quad as (25 x 25) world units in dimension, it is (25 x 25) pixels in dimension.

或者不用沿世界x轴按50个世界单位进行平移,而是沿屏幕x轴(向右)按50个像素进行平移.

Or instead of translating by 50 world units along the world x-axis, you translate by 50 pixels along the screen x-axis (to the right).

那么您如何创建正交投影?

首先,它们通常使用以下参数进行定义:

First, they are usually defined using the following parameters:

  • left-左垂直裁剪平面的X坐标
  • right-右垂直裁剪平面的X坐标
  • bottom-底部水平裁剪平面的Y坐标
  • top-顶部水平裁剪平面的Y坐标
  • near-接近深度裁剪平面
  • far-深度剪切平面
  • left - X coordinate of the left vertical clipping plane
  • right - X coordinate of the right vertical clipping plane
  • bottom - Y coordinate of the bottom horizontal clipping plane
  • top - Y Coordinate of the top horizontal clipping plane
  • near - Near depth clipping plane
  • far - Far depth clipping plane

请记住,所有单位均以像素为单位.典型的正交投影定义为:

Remember, all units are in pixels. A typical Orthographic Projection would be defined as:

glOrtho(0.0, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);

假设您不(或不能)使用

Assuming you do not (or can not) make use of glOrtho (you have your own Matrix class or another reason), then you must calculate the Orthographic Projection matrix yourself.

正交矩阵定义为:

2/(r-l)       0           0       -(r+l)/(r-l)
   0       2/(t-b)        0       -(t+b)/(t-b)
   0          0       -2/(f-n)    -(f+n)/(f-n)
   0          0           0            1

来源A 来源B

在这一点上,除非您决定使用自己的数学库,否则我建议使用预制的数学库.我在实践中看到的最常见的bug来源之一是与矩阵有关的,您花费在调试矩阵上的时间越少,您就必须花费更多的时间来专注于其他更有趣的工作.

At this point I recommend using a pre-made mathematics library unless you are determined to use your own. One of the most common bug sources I see in practice are matrix-related and the less time you spend debugging matrices, the more time you have to focus on other more fun endeavors.

GLM 是一个广泛使用且受人尊敬的库,它已建立为GLSL功能建模.在此处100行.

GLM is a widely-used and respected library that is built to model GLSL functionality. The GLM implementation of glOrtho can be seen here at line 100.

如何使用正交投影?

正交投影通常用于在3D场景的顶部渲染GUI.使用以下模式可以很容易地做到这一点:

Orthographic projections are commonly used to render a GUI on top of your 3D scene. This can be done easily enough by using the following pattern:

  1. 清除缓冲区
  2. 应用透视投影矩阵
  3. 渲染3D对象
  4. 应用正交投影矩阵
  5. 渲染2D/GUI对象
  6. 交换缓冲区


旧答案

请注意,这回答了错误的问题.它假定问题归结为如何从屏幕空间转换为NDC空间?".万一有人搜索出现此问题以寻找答案,就剩下它了.

目标是从屏幕空间转换为NDC空间.因此,我们首先定义这些空间是什么,然后我们可以创建一个转换.

The goal is convert from Screen Space to NDC Space. So let's first define what those spaces are, and then we can create a conversion.

标准化设备坐标

NDC空间只是对剪辑空间中的顶点执行透视分割的结果.

NDC space is simply the result of performing perspective division on our vertices in clip space.

clip.xyz /= clip.w

clip是剪辑空间中的坐标.

Where clip is the coordinate in clip space.

这是将所有未剪切的顶点放入一个单位立方体(在所有轴上在[-1, 1]的范围内),屏幕中心在(0, 0, 0)处.剪切的所有顶点(位于视锥范围之外)均不在此单位立方体内,并且会被GPU抛弃.

What this does is place all of our un-clipped vertices into a unit cube (on the range of [-1, 1] on all axis), with the screen center at (0, 0, 0). Any vertices that are clipped (lie outside the view frustum) are not within this unit cube and are tossed away by the GPU.

在OpenGL中,此步骤作为原始组装(D3D11是在光栅化程序阶段).

In OpenGL this step is done automatically as part of Primitive Assembly (D3D11 does this in the Rasterizer Stage).

屏幕坐标

只需通过将归一化的坐标扩展到视口范围即可计算屏幕坐标.

Screen coordinates are simply calculated by expanding the normalized coordinates to the confines of your viewport.

screen.x = ((view.w * 0.5) * ndc.x) + ((w * 0.5) + view.x)
screen.y = ((view.h * 0.5) * ndc.y) + ((h * 0.5) + view.y)
screen.z = (((view.f - view.n) * 0.5) * ndc.z) + ((view.f + view.n) * 0.5)

在哪里

  • screen是屏幕空间中的坐标
  • ndc是归一化空间中的坐标
  • view.x是视口x原点
  • view.y是视口y原点
  • view.w是视口宽度
  • view.h是视口高度
  • view.f是远处的视口
  • view.n是附近的视口
  • screen is the coordinate in screen-space
  • ndc is the coordinate in normalized-space
  • view.x is the viewport x origin
  • view.y is the viewport y origin
  • view.w is the viewport width
  • view.h is the viewport height
  • view.f is the viewport far
  • view.n is the viewport near

从屏幕转换为NDC

由于上面已经有从NDC到屏幕的转换,因此很容易计算反向.

As we have the conversion from NDC to Screen above, it is easy to calculate the reverse.

ndc.x = ((2.0 * screen.x) - (2.0 * x)) / w) - 1.0
ndc.y = ((2.0 * screen.y) - (2.0 * y)) / h) - 1.0
ndc.z = ((2.0 * screen.z) - f - n) / (f - n)) - 1.0

示例:

viewport (w, h, n, f) = (800, 600, 1, 1000)

screen.xyz = (400, 300, 200)
ndc.xyz = (0.0, 0.0, -0.599)

screen.xyz = (575, 100, 1)
ndc.xyz = (0.4375, -0.666, -0.998)

进一步阅读

有关所有变换空间的更多信息,请阅读 OpenGL变换.

For more information on all of the transform spaces, read OpenGL Transformation.

编辑评论

在对原始问题的评论中,Bo将屏幕空间原点指定为左上角.

In the comment on the original question, Bo specifies screen-space origin as top-left.

对于OpenGL,视口原点(以及屏幕空间原点)位于左下角.参见 glViewport .

For OpenGL, the viewport origin (and thus screen-space origin) lies at the bottom-left. See glViewport.

如果像素坐标确实是左上角的原点,则在将screen.y转换为ndc.y时需要考虑到这一点.

If your pixel coordinates are truly top-left origin then that needs to be taken into account when transforming screen.y to ndc.y.

ndc.y = 1.0 - ((2.0 * screen.y) - (2.0 * y)) / h)

如果要将屏幕/gui上的鼠标单击的坐标转换为NDC空间(作为对世界空间的完全转换的一部分),则需要此方法.

This is needed if you are transforming, say, a coordinate of a mouse-click on screen/gui into NDC space (as part of a full transform to world space).

这篇关于从像素到NDC的转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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