0

OpenGL 3.x です。技術面で遅れをとりたくないからです。

まず第一に、はい、私はそれがたくさんあることを知っています. 機能が問題ないことはほぼ確実です。vec3 transform(vec3)他に何もないとしても、私がここに来た問題が含まれていないことはわかっています。

私が問題を抱えているコードのビットは、関数にあります(またはあるべきです)vec3 project(vec3)。たとえば、ボックスを直接見ている場合は、問題ないように見えます。ボックスが画面の側面に近づくようにカメラを少し回転させると (周辺視野)、ハッピー ボックスの開始点が長方形になります。それは私が入れているゲームで私が一緒に暮らすことができるものですが、それは面倒です.

投影の背後にある基本的な理論は次のとおりです。ポイント (x、y、z) があり、それと原点 (カメラがある場所) の間の角度を見つけ、nearz離れた平面に投影します。角度を見つけることは と の問題angleX = atan(x/z)ですangleY = atan(y/z)。これらの 2 つの角度を使用して、 を実行して近平面に投影しますpoint = tan(angle) * nearzedgeY = tan(fovy) * nearz次に、とで画面の外側の尾根を見つけますedgeX = tan(fovy * aspect) * nearz。を使用してスクリーンポイントを見つけるscreen = point/edge

私が持っていた基本的な最適化として、これを修正するために削除したのはscreen = angle/fov

私の射影関数の理論に何か問題がありますか? 実装は次のとおりです。

#version 330

uniform vec3 model_location = vec3(0.0, 0.0, 0.0);
uniform vec3 model_rotation = vec3(0.0, 0.0, 0.0);
uniform vec3 model_scale    = vec3(1.0, 1.0, 1.0);

uniform vec3 camera_location = vec3(0.0, 0.0, 0.0);
uniform vec3 camera_rotation = vec3(0.0, 0.0, 0.0);
uniform vec3 camera_scale    = vec3(1.0, 1.0, 1.0);

uniform float fovy   =   60.0;
uniform float nearz  =    0.01;
uniform float farz   = 1000.0;
uniform float aspect =    1.0;

vec3 transform(vec3 point)
{
    vec3 translate = model_location - camera_location;
    vec3 rotate    = radians(model_rotation);
    vec3 scale     = model_scale    / camera_scale;

    vec3 s = vec3(sin(rotate.x), sin(rotate.y), sin(rotate.z));
    vec3 c = vec3(cos(rotate.x), cos(rotate.y), cos(rotate.z));

    float sy_cz = s.y * c.z;
    float sy_sz = s.y * s.z;
    float cx_sz = c.x * s.z;

    vec3 result;
    result.x = ( point.x * ( ( c.y * c.z ) * scale.x ) ) + ( point.y * ( ( ( -cx_sz )    + ( s.x * sy_cz ) )    * scale.y ) ) + ( point.z * ( ( ( -s.x * s.z ) + ( c.x * sy_cz ) ) * scale.z ) ) + translate.x;
    result.y = ( point.x * ( ( c.y * s.z ) * scale.y ) ) + ( point.y * ( ( ( c.x * c.z ) + ( s.x * sy_sz ) )    * scale.y ) ) + ( point.z * ( ( ( -s.x * c.z ) + ( c.x * sy_sz ) ) * scale.z ) ) + translate.y;
    result.z = ( point.x * ( ( -s.y )      * scale.x ) ) + ( point.y * ( (   s.x * c.y )                        * scale.y ) ) + ( point.z * ( (    c.x * c.y )                     * scale.z ) ) + translate.z;

    return result;
}

vec4 project(vec3 point)
{
    vec4 result = vec4(0.0);

    vec3 rotation = radians(camera_rotation);

    result.x = ( atan(point.x/point.z) - rotation.y );
    result.y = ( atan(point.y/point.z) - rotation.x );
    result.z = point.z/(farz - nearz);
    result.w = 1.0;

    result.x = tan(result.x) * nearz;
    result.y = tan(result.y) * nearz;

    vec2 bounds = vec2( tan(fovy * aspect) * nearz,
                        tan(fovy) * nearz );

    result.x = result.x / bounds.x;
    result.y = result.y / bounds.y;

    if (camera_rotation.z == 0)
        return result;

    float dist = sqrt( (result.x*result.x) + (result.y*result.y) );
    float theta = atan(result.y/result.x) + rotation.z;
    result.x = sin(theta) * dist;
    result.y = cos(theta) * dist;

    return result;
}

layout(location = 0) in vec3 vertex_position;
layout(location = 1) in vec2 texCoord;

out vec2 uvCoord;

void main()
{
    uvCoord = texCoord;
    vec4 pos = project( transform(vertex_position) );

    if (pos.z < 0.0)
        return;
    gl_Position = pos;
}

予想されるいくつかの質問に答えるには:

Q: なぜ GLM/some-other-mathematics-lib を使わないのですか?

A:

- ちょっと前にやってみた。私の「ハローワールド!」三角形が画面中央に引っかかっていました。変換行列を使用しても、前に戻したり、スケーリングしたり、何もしませんでした。

-自分自身のために物事を理解する方法を学ぶことが重要だから. これを行うということは、すべてが手に負えなくなった場合に頼りになる何かを持ちながら、このようなことに取り組む方法を学ぶことを意味します. (この愚か者の正当化があります。)

Q: なぜ行列を使用しないのですか?

A:

-彼らも私を嫌っています。

-私は新しい方法でそれをやっています.マトリックスを使用した場合、自分でそれを理解するのではなく、すべてのチュートリアルがそれを行うと言っているとおりにそれを行うことになります.

試したソース:

http://ogldev.atspace.co.uk/index.html

http://www.swiftless.com/opengltuts/opengl4tuts.html

ページの「OpenGL シェーディング言語第 3 版」、「OpenGL 固定機能のエミュレート」から GLSL シェーダ (頂点とフラグ) を文字ごとにコピーしました。288-293

それぞれを何度も試し、それぞれをいじくり回して狂気に陥りました。戦争ゲームをプログラムしようとして、ワイヤーフレームのボックスを手に入れて、平和のシンボルに投影しました。

編集:

問題は、datenwolf が指摘した極座標の使用であることが判明しました。あまり進んでいない数学を使用した射影のためのより良い方程式は次のc = zNear * (p.x/p.y) とおりです。その結果、同じ角度を使用します。

投影される点に X と Y が与えられ、それらの前部分三角形の辺にそれぞれ A と C のラベルが付けられていると仮定します。atan(Y/X) = angle等式のandを取得できます。それatan(C/A) = angleから 、A は近くの平面までの距離で終わります。C は Y 方向のスクリーン座標です。atan(Y/X) = atan(C/A)Y/X = C/AC = A * (Y/X)

4

1 に答える 1

1

-彼らも私を嫌っています。

行列はあなたの友達です。それらの使い方を学びましょう。

-私は新しい方法でそれをやっています.マトリックスを使用した場合、自分でそれを理解するのではなく、すべてのチュートリアルがそれを行うと言っているとおりにそれを行うことになります.

あなたのやり方は悪いです。変換は通用せず、非常に厳格なフレームワークに自分自身を閉じ込めています. これも:

   result.x = ( point.x * ( ( c.y * c.z ) * scale.x ) ) + ( point.y * ( ( ( -cx_sz )    + ( s.x * sy_cz ) )    * scale.y ) ) + ( point.z * ( ( ( -s.x * s.z ) + ( c.x * sy_cz ) ) * scale.z ) ) + translate.x;
   result.y = ( point.x * ( ( c.y * s.z ) * scale.y ) ) + ( point.y * ( ( ( c.x * c.z ) + ( s.x * sy_sz ) )    * scale.y ) ) + ( point.z * ( ( ( -s.x * c.z ) + ( c.x * sy_sz ) ) * scale.z ) ) + translate.y;
   result.z = ( point.x * ( ( -s.y )      * scale.x ) ) + ( point.y * ( (   s.x * c.y )                        * scale.y ) ) + ( point.z * ( (    c.x * c.y )                     * scale.z ) ) + translate.z;

事実上、回転行列の乗算の後に平行移動行列が続き、非常に複雑でエラーが発生しやすい方法で記述されます。また、貴重な GPU リソースを浪費しています。

あなたの投影関数は、ある種の球状投影モデルを実装しているようです。球面座標は曲線であるため、これは問題です。プリミティブが小さい限り、曲率と比較してうまくいきます。しかし、プリミティブが大きくなるとすぐに、すべての地獄が解き放たれます。プリミティブのエッジは画面上で直線として描画されますが、曲線座標系で変換すると曲線でなければならないからです。これを機能させるには、少なくともいくつかのテッセレーション シェーダーと反復的な頂点調整が必要です。

于 2013-02-05T19:19:00.310 に答える