web-dev-qa-db-ja.com

OpenGL:スケールしてから翻訳しますか?そしてどうやって?

2Dジオメトリがあります。ジオメトリの周囲に境界のある四角形を取り、平面の別の場所にそれの小さいバージョンをレンダリングしたいと思います。これが多かれ少なかれ私がスケーリングと変換をしなければならないコードです:

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
float translateX = dest.x - source.x;
float translateY = dest.y - source.y;

glScalef(scaleX, scaleY, 0.0);
glTranslatef(translateX, translateY, 0.0);
// Draw geometry in question with its normal verts.

これは、dest Originが0の場合、指定された次元で期待どおりに機能します。ただし、xのOriginがゼロ以外の場合、結果は正しくスケーリングされますが(?)のように見えますが、その軸ではゼロに近いものに変換されます。とにかく-dest.xがゼロの場合とまったく同じではないことがわかります。

誰かが私が欠けている明らかなことを指摘できますか?

ありがとう!

最終更新以下のバーバーとマーカスの回答に従って、私はさらに実験を行い、これを解決しました。 Adam Bowenのコメントは先端でした。 2つの重要な事実が欠けていました。

  1. 気になるジオメトリの中心を中心にスケーリングする必要がありました。
  2. (私にとって)直感とは逆の順序で変換を適用する必要がありました。

1つ目は、振り返ってみると明らかです。しかし、後者の場合、私のような他の優れたプログラマー/悪い数学者の場合:私の直感は、絶対平面が存在する Red Book が「大規模な固定座標系」と呼ぶもので動作していたことが判明、および変換を使用して、ジオメトリがその平面上を動き回ります。これは問題ありませんが、複数の変換を1つの行列にスタックする背後にある数学の性質を考えると、物事が実際に機能する方法の逆です(以下の回答またはレッドブックを参照)。基本的に、変換は、コードでの表示とは「逆の順序」で「適用」されます。これが最終的な解決策です:

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
Point sourceCenter = centerPointOfRect(source);
Point destCenter = centerPointOfRect(dest);

glTranslatef(destCenter.x, destCenter.y, 0.0);
glScalef(scaleX, scaleY, 0.0);
glTranslatef(sourceCenter.x * -1.0, sourceCenter.y * -1.0, 0.0);
// Draw geometry in question with its normal verts.
23
Ben Zotto

OpenGLでは、指定した行列は既存の行列の右側に乗算され、頂点は式の右端にあります。

したがって、指定する最後の操作は、ジオメトリ自体の座標系にあります。 (1つ目は通常、ビュー変換です。つまり、カメラのワールド変換の逆です。)

バーバーは、スケーリングの中心点を考慮する必要があるという点で優れています。 (または回転のピボットポイント)。通常、そこで平行移動し、回転/スケールしてから、元に戻します。 (または一般に、基底変換、演算、次に逆を適用します)。これは 根拠の変更 と呼ばれ、読み進めておくとよいでしょう。

とにかく、それがどのように機能するかについて直感を得るために、いくつかの単純な値(ゼロなど)を試してから、それらをわずかに(おそらくアニメーション)変更して、出力で何が起こるかを確認します。次に、変換がジオメトリに対して実際に行っていることを確認するのがはるかに簡単になります。

更新

順序が「逆」になっていること。直感は、初心者のOpenGLコーダーの間ではかなり一般的です。私はコンピュータグラフィックスのコースを指導してきましたが、多くは同じように反応します。変換とジオメトリのツリー(シーングラフ)をレンダリングする際にpushmatrix/popmatrixの使用を検討すると、OpenGLがどのように実行するかを考えるのが簡単になります。すると、現在の順序はかなり自然になり、その逆は、何か有用なことを成し遂げることをかなり難しくします。

18
Macke

回転と同様に、スケールは原点から動作します。したがって、セグメント[10:20]にまたがるオブジェクトの半分(X軸など)でスケーリングすると、[5:10]になります。したがって、オブジェクトはスケーリングされ、原点に近づきました。まさにあなたが観察したもの。

一般に、最初にスケールを適用するのはこのためです(オブジェクトは0付近で定義される傾向があるためです)。

したがって、中心点を中心にオブジェクトをスケーリングする場合は、オブジェクトを中心から原点に移動し、そこでスケーリングして、元に戻すことができます。

注:最初に翻訳してから拡大縮小すると、拡大縮小は前の翻訳に適用されるため、この方法で問題が発生した可能性があります。

6
Bahbar

私はOpenGL ESで遊んだことがなく、少しOpenGLで遊んだだけです。

確かに、原点とは異なる位置から変換したいようですが、変換を実行して、そのビットを glPushMatrix()およびglPopMatrix() 内に描画できますか?

e.g.

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
float translateX = dest.x - source.x;
float translateY = dest.y - source.y;

glPushMatrix();
  glScalef(scaleX, scaleY, 0.0);
  glTranslatef(translateX, translateY, 0.0);
  // Draw geometry in question with its normal verts.
  //as if it were drawn from 0,0
glPopMatrix();

ポイントを説明するために私が書いた簡単な処理スケッチは次のとおりです。

import processing.opengl.*;
import javax.media.opengl.*;


void setup() {
  size(500, 400, OPENGL);
}

void draw() {
  background(255);
  PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;
  GL gl = pgl.beginGL();  


  gl.glPushMatrix();
    //transform the 'pivot'
    gl.glTranslatef(100,100,0);
    gl.glScalef(10,10,10);
    //draw something from the 'pivot'
    gl.glColor3f(0, 0.77, 0);
    drawTriangle(gl);
  gl.glPopMatrix();
  //matrix poped, we're back to orginin(0,0,0), continue as normal
  gl.glColor3f(0.77, 0, 0);
  drawTriangle(gl);
  pgl.endGL();
}

void drawTriangle(GL gl){
  gl.glBegin(GL.GL_TRIANGLES);
  gl.glVertex2i(10, 0);
  gl.glVertex2i(0, 20);
  gl.glVertex2i(20, 20);
  gl.glEnd();
}

これは、実行中のスケッチの画像です。同じ緑色の三角形が描画され、平行移動とスケールが適用されています。次に、赤い三角形がプッシュ/ポップ「ブロック」よりも外側にあるため、変換の影響を受けません。

alt text

HTH、ジョージ

1
George Profenza