从二维点计算单位球上的三维坐标

5
我有一个正方形的圆形位图,我想计算所有像素的法线,就像它是半径为1的球体一样:

enter image description here

球体/圆形位于位图中心。

这个方程式是什么?


他们通常不会为从图片生成几何图形而制定方程。你要找的是算法。当然,除非你已经有一种方法来找到你认为在那张图片中心点。如果是这样,请重新表述问题。 - Merlyn Morgan-Graham
1
不,这是那种周末脑子一片空白的问题之一,我有所有信息,也可能有我高中时代的所有数学知识,但我已经盯着这个问题几个小时了,却无法写出一行代码。目的:为我的业余游戏中的一些球形广告牌制作法线图和深度图。 - Will
你想要一个以原点为中心或任意点(x,y,z)为中心的球的解决方案吗?第一个要简单得多。 - Tom Zych
中心已知。在二维平面上,其X、Y坐标为W/2和H/2,在三维空间中,其中心坐标为0,0,0(我们需要法线并且它是一个单位球体)。 - Will
是的,我想为一个球制作法线贴图和深度贴图。第一步是法线贴图;因为它是一个单位球体,所以每个像素的法线就是其(面向前方)的三维坐标。一旦我知道了每个像素的x、y、z坐标,深度就很容易了。 - Will
显示剩余4条评论
4个回答

8

我不太了解人们如何编写3D程序,所以我只提供纯数学知识,希望有用。

半径为1,以原点为中心的球体是满足以下条件的点的集合:

x2 + y2 + z2 = 1

我们想要找到一个在该球体上已知x和y坐标的点的三维坐标。因此,只需解出z:

z = ±sqrt(1 - x2 - y2)。

现在,让我们考虑一个从球体外指向外部的单位向量。它是一个单位球体,因此我们可以使用从原点到(x,y,z)的向量,即 <x,y,z>。

现在,我们想要在(x,y,z)处求球体的切平面方程,但这将使用其自己的x、y和z变量,因此我将使其在(x0,y0,z0)处切于球体。这很简单:

x0x + y0y + z0z = 1

希望这有所帮助。


(原帖作者):

你的意思是像这样:

const int R = 31, SZ = power_of_two(R*2);
std::vector<vec4_t> p;
for(int y=0; y<SZ; y++) {
    for(int x=0; x<SZ; x++) {
        const float rx = (float)(x-R)/R, ry = (float)(y-R)/R;
        if(rx*rx+ry*ry > 1) { // outside sphere
            p.push_back(vec4_t(0,0,0,0));
        } else {
            vec3_t normal(rx,sqrt(1.-rx*rx-ry*ry),ry);
            p.push_back(vec4_t(normal,1));
        }
    }
}

如果我将法线视为颜色并将其绘制,它确实会产生一个漂亮的球形阴影效果;是这样吗?


(TZ)

很抱歉,我不熟悉 C++ 的这些方面。很长时间以前我使用过该语言,但最近没有使用。


+1:非常好的答案。但是你为什么要在最后提到飞机呢? - Troubadour
我不确定是否需要它,但很容易推导出来。我上一次尝试这种程序是在90年代,我不知道通常的做法是什么。 - Tom Zych

2
这个公式经常用于“假环境映射”效果。
double x = 2.0 * pixel_x / bitmap_size - 1.0;
double y = 2.0 * pixel_y / bitmap_size - 1.0;
double r2 = x*x + y*y;
if (r2 < 1)
{
    // Inside the circle
    double z = sqrt(1 - r2);
    .. here the normal is (x, y, z) ...
}

1

显然,由于缺少维度,您只能假设所有点都在球的一半或类似位置,因此受到限制。除此之外,这很简单。

圆的中心具有正好朝向内部或外部的法线,垂直于绘制圆的平面。

圆边缘上的每个点都朝向中心,因此您可以计算出该点的法线。

对于位于中心和边缘之间的任何点,您使用距离中心的距离和一些简单的三角函数(我暂时无法想起)进行计算。在某些点上,lerp大致准确,但不完全符合您的需求,因为它是曲线。不过,这是一个简单的曲线,并且您知道开始和结束值,因此找出它们只需要一个简单的方程。


(目前我还无法理解)是的,它也一直困扰着我 :) - Will
从位置和角度找到向量的数学公式在每个第一人称摄像机实现中都是相同的。我想它是sin和cos,X * sin(a) + Y * cos(A),对吗? - ssube

1

我想我明白你想做什么:为图像生成深度数据的网格。有点像对球进行光线追踪。

在这种情况下,你需要一个射线-球体相交测试:

http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter1.htm

你的光线将是简单的垂直光线,基于你的U/V坐标(乘以2,因为你的球体直径为2)。这将给你球体正面的点。

从那里开始,按照下面的方法计算法线(点 - 原点,半径已经是1个单位)。

从上面的链接中剪切而来:

你必须结合两个方程:

  • 光线:R(t) = R0 + t * Rd,t > 0,其中R0 = [X0,Y0,Z0],Rd = [Xd,Yd,Zd]
  • 球体:S = 点集[xs,ys,zs],其中(xs - xc)2 + (ys - yc)2 + (zs - zc)2 = Sr2

为此,计算你的光线(x *像素/宽度,y *像素/宽度,z: 1),然后:

  • A = Xd^2 + Yd^2 + Zd^2
  • B = 2 * (Xd * (X0 - Xc) + Yd * (Y0 - Yc) + Zd * (Z0 - Zc))
  • C = (X0 - Xc)^2 + (Y0 - Yc)^2 + (Z0 - Zc)^2 - Sr^2

将其代入二次方程:

  • t0,t1 = (- B + (B^2 - 4*C)^1/2) / 2

检查判别式(B^2 - 4*C),如果有实根,则交点为:

  • Ri = [xi, yi, zi] = [x0 + xd * ti , y0 + yd * ti, z0 + zd * ti]

表面法线为:

  • SN = [(xi - xc)/Sr, (yi - yc)/Sr, (zi - zc)/Sr]

总结:

所以,既然我们谈论单位值和指向Z的光线(没有x或y分量),我们可以将这些方程简化:

光线:

  • X0 = 2 * pixelX / width
    Y0 = 2 * pixelY / height
    Z0 = 0

  • Xd = 0
    Yd = 0
    Zd = 1

球体:

  • Xc = 1
    Yc = 1
    Zc = 1

因素:

  • A = 1(单位射线)
  • B
    = 2 *(0 + 0 +(0-1))
    = -2(没有X / Y分量)
  • C
    =(X0-1)^ 2 +(Y0-1)^ 2 +(0-1)^ 2-1
    =(X0-1)^ 2 +(Y0-1)^ 2

  • 判别式
    =(-2)^ 2-4 * 1 * C
    = 4-4 * C

从这里开始:

If discriminant < 0:
  Z = ?, Normal = ?
Else:
  t = (2 + (discriminant) ^ 1 / 2) / 2
If t < 0 (hopefully never or always the case)
  t = -t

然后:

  • Z:t
  • Nx:Xi - 1
    Ny:Yi - 1
    Nz:t - 1

更进一步的推导:

直觉上看,C(X^2 + Y^2)和平方根是这里最突出的数字。如果我对我的数学(特别是对和的指数进行变换)有更好的回忆,那么我敢打赌我可以将其推导到Tom Zych给你的内容。由于我不能,所以我会像上面那样留下它。


顺便说一句,我不声称这里有任何效率;) 可能有更快的方法来计算这个(简单的三角学,如sin(x),cos(y)的组合,反之亦然,似乎是有序的)。 - Merlyn Morgan-Graham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接