質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
GLSL

GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

Q&A

解決済

2回答

651閲覧

GLSLのfor文が読めない -> for(;++i<99.&&d>.001;)

defunty

総合スコア8

GLSL

GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

0グッド

0クリップ

投稿2022/02/20 19:27

レイマーチングを勉強しており、以下を参考にコードを写経しています。
https://neort.io/product/bvcrf5s3p9f7gigeevf0

その中で、以下のようなコードが書かれていました。

float map(vec3 p){ ... } void main() { ... float d=1.,i; for(;++i<99.&&d>.001;)p+=rd*(d=map(p)); ... }

この for(;++i<99.&&d>.001;)p+=rd*(d=map(p)); について何をしているか分からず調べてみたのですが、参考になりそうなドキュメントを見つけることができませんでした。

想像で「こんなことをしているのかな」とコードを適当に書き換えてみたのですが、書き換える前とは異なる結果になりました。

for( float i=0.; i<99.&&d>.001; i++) { p+=rd*(d=map(p)); }

このfor文ではどのようなことが起きているのか、分かる方がいましたら教えていただけますでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2022/02/20 23:29

> 何をしているか分からず 具体的にどこがわからないなどありますか?
vann_2921

2022/02/21 00:14

for文の書き方は分かりますか? その場合その知っているfor文の書き方とどこが違っていて理解できないのか説明できますか?
hoshi-takanori

2022/02/21 00:25

元のコードは i をループの先頭で増やしてるのに対して、書き換えたものはループの最後で増やしてます。それにより、書き換えたものではループを抜けた時に i が 0 になる (そして次の割り算で 0 割りが起こる) 可能性があるかと。 あと、元のコードでは i (と fragColor) が初期化されてないのが気になります…。
defunty

2022/02/21 01:42 編集

> 具体的にどこがわからないなどありますか? > for文の書き方は分かりますか? > その場合その知っているfor文の書き方とどこが違っていて理解できないのか説明できますか? for文の基本的な書き方は理解しています。 私が認識しているfor文は以下のようなシンタックスです。 ``` for( float i=0.; i<99.&&d>.001; i++) { ... } ``` これに対して、参考元のコードでは `for(;++i<99.&&d>.001;)` というようにiを初期化する式とiの加算式を省略しているように見えます。 しかし、初期化式を `float i=0.` 、加算式を `i++` と仮定してfor文を書いたところ、元のコードと異なる結果になったため、元のコードでは「iは何の値から始まるか」・「for文の中でiはどのように変化していくか」・「 for文終了させる条件式は `i<99.&&d>.001` で合っているかどうか」という三点が分かっていません。
hoshi-takanori

2022/02/21 01:53

for 文の初期化式、継続条件、更新式はどれも省略可能で、元のコードでは初期化式と更新式が省略されてます。 初期化式が省略されると for 文以前の値がそのまま使われます。(が、i は初期化されてない…。) また、更新式も省略されていて、代わりに継続条件の中に ++i があるため、普通はループの最後に i を増やすことが多いですが、まず i を増やしてから継続条件を評価してます。
defunty

2022/02/21 02:12 編集

> 元のコードは i をループの先頭で増やしてるのに対して、書き換えたものはループの最後で増やしてます。 > それにより、書き換えたものではループを抜けた時に i が 0 になる (そして次の割り算で 0 割りが起こる) 可能性があるかと。 ループの最後にiを増加させるという書き方があるんですね。ご指摘内容を参考に、for文を書き換えてみたのですが、結果は変わらずでした… ``` for( float i=0.; i<99.&&d>.001; ++i) { ... } ``` 元のコードはそのままどこでも動くというものではないため、Shadertoyに少し補完して移すと意図通り動くのですが、for文を書き換えると影のない白い五角形の立体が表示されるはずです。 以下、Shadertoyで実際に動くプログラムのリンクです。 https://www.shadertoy.com/view/NssfWj
fana

2022/02/21 02:14

iの初期値を0だと仮定し,且つ,dの方の条件をとりあえず無視して考えると, 大元のコードでは 98回, 「適当に書き換えてみた」側では99回ループする ということになりますね.
defunty

2022/02/21 02:30

> iの初期値を0だと仮定し,且つ,dの方の条件をとりあえず無視して考えると, > 大元のコードでは 98回, > 「適当に書き換えてみた」側では99回ループする すみません、説明し忘れていたのですが、大元のコードと「適当に書き換えてみた」結果とではfor文が一回分多いというようなレベルの違いではなく、全く異なる種別の結果となっています。 for文の中ではベクトルに関する計算を行い各種座標に光があたるかどうかを判別していき、回数が増えれば増える程、影の精度が増すのですが、99回行っても98回行ってもほぼ違いはありません。 ですが、「適当に書き換えてみた」側だと一切影がつかずに真っ白なオブジェクトとなってしまっている状態です。 以下Shadertoyのリンクから実際のコードを触っていただければ分かりやすいと思います。 https://www.shadertoy.com/view/NssfWj
fana

2022/02/21 02:48

for( i = 0.;i<98.&&d>.001; ++i) { p+=rd*(d=map(p)); } ++i; で大元と同じ動作になるように見えます.
hoshi-takanori

2022/02/21 02:48

あ、そうか。for で i を宣言すると、for の外にある i とは別の i ができて、ループの制御は for で宣言したものが使われるけど、for の後ろにある 3. / i の i は for の外の i だからですね…。
fana

2022/02/21 02:52

・forの初期化式に float i=0 と書いてしまうと,ループの外のiとは別物になってしまう. ・(あと,示されたリンク先では ++ が2か所に入ってますよ) ・ループの次にある i で除す式における i の値が結果に大きく効いている様子なので,iの値を大元と合わせるには +1 する必要があると見えます.(しないとハイライトがでかくなる様子)
defunty

2022/02/21 02:55

理解できました、ありがとうございます。 ループを抜けた後に参照するiがループを抜けた時点のiになるものと勘違いしていました。 上記書いていただいたコードで意図した結果が得られることを確認できました。
fana

2022/02/21 02:57

i の値がループ後に使用されることはリンクを踏まないとわからないのが問題ですね…… (この質問に提示された範囲だけ見ていると,単なるループ回す用のカウンタにしか見えない)
hoshi-takanori

2022/02/21 02:58

あと、ブラウザによって挙動が違った (M1 Mac の Safari だとそのままで表示されたけど、Firefox では真っ黒だった) ので、やはり変数 (i と fragColor) の初期化は必要かと。
guest

回答2

0

ベストアンサー

for文

for (/*1*/ ; /*2*/ ; /*4*/) /*3*/

の実行順序は

1 -> 2 --- (2が真なら) ---> 3 -> 4 -> 2 -> ...

です。1~4はそれぞれ空でも構いません。(2が空の場合は真として扱う)


想像で「こんなことをしているのかな」とコードを適当に書き換えてみたのですが、書き換える前とは異なる結果になりました。

話を単純にするためにdに関わるものを消して、iの条件式の右辺を2に減らすと

C

1int i=0; 2for(; ++i < 2;) printf("i = %d\n", i);

これは1回ループしますが

C

1for(int i = 0; i < 2; ++i) printf("i = %d\n", i);

こっちは2回ループしてループ回数が異なります。


すみません、説明し忘れていたのですが、大元のコードと「適当に書き換えてみた」結果とではfor文が一回分多いというようなレベルの違いではなく、全く異なる種別の結果となっています。

for(float i=0, ...としているので、もとのコードとはforを抜けた後のiの値が大きく異なります。

C

1#include <stdio.h> 2int main(void){ 3 float i; 4 for(float i=0;i<10;i++); 5 printf("%f",i); // 0 6}

投稿2022/02/21 02:06

編集2022/02/21 02:37
ozwk

総合スコア13528

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

defunty

2022/02/21 02:52

確かにご指摘の通りでした。 ループを抜けた後に参照するiがループを抜けた時点のiになるものと勘違いしていました。 ありがとうございます。
guest

0

特段、レイマーチングに詳しい訳ではありませんが・・

for( float i=0.; i<99.&&d>.001; i++) {

for 文をもう少し展開してみました。基本的には99回繰り返すループになっています。

glsl

1for (; i < 99. && d > .001; i++) { 2 d = map(p); // 球体までの距離を求める距離関数 3 p += rd * d; // レイの先端位置を算出 4}

<参考>
■ Online JavaScript Beautifier
https://beautifier.io/
※ JavaScript 用のコード整形サービスですが、GLSL は C言語の構文の為、ある程度、コードを見やすく表示させることが可能です。

■レイマーチングで球体を描く | wgld.org
https://wgld.org/d/glsl/g009.html

投稿2022/02/20 23:01

cx20

総合スコア4633

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問