レイマーチングを勉強しており、以下を参考にコードを写経しています。
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文ではどのようなことが起きているのか、分かる方がいましたら教えていただけますでしょうか。
> 何をしているか分からず
具体的にどこがわからないなどありますか?
for文の書き方は分かりますか?
その場合その知っているfor文の書き方とどこが違っていて理解できないのか説明できますか?
元のコードは i をループの先頭で増やしてるのに対して、書き換えたものはループの最後で増やしてます。それにより、書き換えたものではループを抜けた時に i が 0 になる (そして次の割り算で 0 割りが起こる) 可能性があるかと。
あと、元のコードでは i (と fragColor) が初期化されてないのが気になります…。
> 具体的にどこがわからないなどありますか?
> 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` で合っているかどうか」という三点が分かっていません。
for 文の初期化式、継続条件、更新式はどれも省略可能で、元のコードでは初期化式と更新式が省略されてます。
初期化式が省略されると for 文以前の値がそのまま使われます。(が、i は初期化されてない…。)
また、更新式も省略されていて、代わりに継続条件の中に ++i があるため、普通はループの最後に i を増やすことが多いですが、まず i を増やしてから継続条件を評価してます。
> 元のコードは i をループの先頭で増やしてるのに対して、書き換えたものはループの最後で増やしてます。
> それにより、書き換えたものではループを抜けた時に i が 0 になる (そして次の割り算で 0 割りが起こる) 可能性があるかと。
ループの最後にiを増加させるという書き方があるんですね。ご指摘内容を参考に、for文を書き換えてみたのですが、結果は変わらずでした…
```
for( float i=0.; i<99.&&d>.001; ++i) {
...
}
```
元のコードはそのままどこでも動くというものではないため、Shadertoyに少し補完して移すと意図通り動くのですが、for文を書き換えると影のない白い五角形の立体が表示されるはずです。
以下、Shadertoyで実際に動くプログラムのリンクです。
https://www.shadertoy.com/view/NssfWj
iの初期値を0だと仮定し,且つ,dの方の条件をとりあえず無視して考えると,
大元のコードでは 98回,
「適当に書き換えてみた」側では99回ループする
ということになりますね.
> iの初期値を0だと仮定し,且つ,dの方の条件をとりあえず無視して考えると,
> 大元のコードでは 98回,
> 「適当に書き換えてみた」側では99回ループする
すみません、説明し忘れていたのですが、大元のコードと「適当に書き換えてみた」結果とではfor文が一回分多いというようなレベルの違いではなく、全く異なる種別の結果となっています。
for文の中ではベクトルに関する計算を行い各種座標に光があたるかどうかを判別していき、回数が増えれば増える程、影の精度が増すのですが、99回行っても98回行ってもほぼ違いはありません。
ですが、「適当に書き換えてみた」側だと一切影がつかずに真っ白なオブジェクトとなってしまっている状態です。
以下Shadertoyのリンクから実際のコードを触っていただければ分かりやすいと思います。
https://www.shadertoy.com/view/NssfWj
for( i = 0.;i<98.&&d>.001; ++i) {
p+=rd*(d=map(p));
}
++i;
で大元と同じ動作になるように見えます.
あ、そうか。for で i を宣言すると、for の外にある i とは別の i ができて、ループの制御は for で宣言したものが使われるけど、for の後ろにある 3. / i の i は for の外の i だからですね…。
・forの初期化式に float i=0 と書いてしまうと,ループの外のiとは別物になってしまう.
・(あと,示されたリンク先では ++ が2か所に入ってますよ)
・ループの次にある i で除す式における i の値が結果に大きく効いている様子なので,iの値を大元と合わせるには +1 する必要があると見えます.(しないとハイライトがでかくなる様子)
理解できました、ありがとうございます。
ループを抜けた後に参照するiがループを抜けた時点のiになるものと勘違いしていました。
上記書いていただいたコードで意図した結果が得られることを確認できました。
i の値がループ後に使用されることはリンクを踏まないとわからないのが問題ですね……
(この質問に提示された範囲だけ見ていると,単なるループ回す用のカウンタにしか見えない)
あと、ブラウザによって挙動が違った (M1 Mac の Safari だとそのままで表示されたけど、Firefox では真っ黒だった) ので、やはり変数 (i と fragColor) の初期化は必要かと。

回答2件
あなたの回答
tips
プレビュー