الانكسار ثلاثي الجوانب في ثلاث خطوات

عندما تقوم بتقديم كائن ثلاثي الأبعاد ، فأنت تحتاج دائمًا إلى إضافة بعض المواد إليه بحيث يكون مرئيًا وتبدو كما تريد ؛ لا يهم إذا قمت بذلك في برامج خاصة أو في الوقت الفعلي عبر WebGL.


يمكن محاكاة معظم المواد باستخدام الأدوات المدمجة للمكتبات مثل Three.js ، لكن في هذا البرنامج التعليمي سأوضح لك كيفية جعل الكائنات تبدو كزجاج في ثلاث خطوات باستخدام - لقد خمنت ذلك - Three.js.


الخطوة 1: الإعداد والانعكاسات الأمامية


في هذا المثال ، سأستخدم هندسة الماس ، لكن يمكنك استخدام مكعب بسيط أو أي شكل آخر.


لنقم بإعداد مشروعنا نحن بحاجة إلى متخيل ، مشهد ، كاميرا ، وهندسة. من أجل تصور سطحنا ، نحتاج إلى مواد. سيكون إنشاء هذه المواد هو الغرض الرئيسي من الدرس. لذلك ، فلنقم بإنشاء كائن SharedMaterial جديد باستخدام تظليل الرأس والجزء.


على عكس توقعاتك ، لن تكون مادتنا شفافة ، في الواقع سنقوم بتشويه ما سيكون وراء الماس. للقيام بذلك ، سنحتاج إلى تصور المشهد (بدون الماس) في الملمس. أنا فقط أعرض طائرة حجم النطاق بالكامل باستخدام كاميرا متعامدة ، ولكن يمكنك أيضًا عرض مشهد بأشياء أخرى. أسهل طريقة لفصل سطح الخلفية عن الألماس في Three.js هي استخدام الطبقات.


this.orthoCamera = new THREE.OrthographicCamera( width / - 2,width / 2, height / 2, height / - 2, 1, 1000 ); //    1  (0    ) this.orthoCamera.layers.set(1); const tex = await loadTexture('texture.jpg'); this.quad = new THREE.Mesh(new THREE.PlaneBufferGeometry(), new THREE.MeshBasicMaterial({map: tex})); this.quad.scale.set(width, height, 1); //      1  this.quad.layers.set(1); this.scene.add(this.quad); 

ستبدو دورة التصور لدينا كما يلي:


 this.envFBO = new THREE.WebGLRenderTarget(width, height); this.renderer.autoClear = false; render() { requestAnimationFrame( this.render ); this.renderer.clear(); //    fbo this.renderer.setRenderTarget(this.envFbo); this.renderer.render( this.scene, this.orthoCamera ); //      this.renderer.setRenderTarget(null); this.renderer.render( this.scene, this.orthoCamera ); this.renderer.clearDepth(); //      this.renderer.render( this.scene, this.camera ); }; 

عظيم ، حان الوقت لرحلة صغيرة في النظرية. المواد الشفافة مثل الزجاج مرئية لأنها تنكسر الضوء. وذلك لأن الضوء يمر عبر الزجاج بشكل أبطأ من خلال الهواء ، وعندما تصطدم شعاع الضوء بمثل هذا الشيء بزاوية ، فإن الفرق في السرعة يتسبب في تغير اتجاه الضوء. هذا التغيير في الاتجاه هو المقصود من الانكسار.



لتكرار ذلك في الكود ، نحتاج إلى معرفة الزاوية بين متجه اتجاه النظرة والسطح العادي. دعنا نغير تظليل قمة الرأس لحساب هذه المتجهات.


 varying vec3 eyeVector; varying vec3 worldNormal; void main() { vec4 worldPosition = modelMatrix * vec4( position, 1.0); eyeVector = normalize(worldPos.xyz - cameraPosition); worldNormal = normalize( modelViewMatrix * vec4(normal, 0.0)).xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } 

في شظية الجزء ، يمكننا الآن استخدام eyeVector و worldNormal كأول معلمتين في وظيفة الانكسار المضمنة في refract . المعلمة الثالثة هي نسبة مؤشرات الانكسار ، أي مؤشر الانكسار (IOR) لزجاجنا المتوسط ​​الكثيف. في حالتنا ، سيكون 1.0 / 1.5 ، ولكن يمكنك تغيير هذه القيمة لتحقيق النتيجة المرجوة. على سبيل المثال ، فإن معامل الانكسار للمياه هو 1.33 ، ومؤشر الماس هو 2.42.


 uniform sampler2D envMap; uniform vec2 resolution; varying vec3 worldNormal; varying vec3 viewDirection; void main() { // get screen coordinates vec2 uv = gl_FragCoord.xy / resolution; vec3 normal = worldNormal; // calculate refraction and add to the screen coordinates vec3 refracted = refract(eyeVector, normal, 1.0/ior); uv += refracted.xy; // sample the background texture vec4 tex = texture2D(envMap, uv); vec4 output = tex; gl_FragColor = vec4(output.rgb, 1.0); } 

https://codesandbox.io/embed/multi-side-refraction-step-13-pzxf9؟fontsize=14&hidenavigation=1&theme=dark


! ممتاز لقد كتبنا بنجاح تظليل. لكن الماس بالكاد مرئي ... جزئيا لأننا قمنا بمعالجة خاصية واحدة فقط من الزجاج. ليس كل الضوء يمر عبره وينكسر ؛ في الواقع ، سينعكس جزء منه. دعونا نرى كيفية تحقيق هذا!


الخطوة 2: تأملات ومعادلة فريسنل


من أجل البساطة ، في هذا الدرس ، لن نحسب الانكسارات الحقيقية ، ولكن ببساطة نستخدم اللون الأبيض للضوء المنكسر. نذهب أبعد من ذلك: كيف يمكنك أن تعرف متى تنعكس ، ومتى تنكسر؟ يعتمد هذا من الناحية النظرية على معامل الانكسار للمادة: عندما تكون الزاوية بين المتجه العارض والسطح الطبيعي أكبر من قيمة العتبة ، سينعكس الضوء.



في التظليل الجزئي ، سنستخدم معادلة فريسنل لحساب النسب بين الأشعة المنعكسة والأشعة المنكسرة. لسوء الحظ ، لا تحتوي glsl على هذه المعادلة ، يمكنك نسخها من هنا:


 float Fresnel(vec3 eyeVector, vec3 worldNormal) { return pow( 1.0 + dot( eyeVector, worldNormal), 3.0 ); } 

يمكننا ببساطة مزج لون نسيج الشعاع المنكسر مع اللون الأبيض المنعكس باستخدام النسبة التي حسبناها للتو.


 uniform sampler2D envMap; uniform vec2 resolution; varying vec3 worldNormal; varying vec3 viewDirection; float Fresnel(vec3 eyeVector, vec3 worldNormal) { return pow( 1.0 + dot( eyeVector, worldNormal), 3.0 ); } void main() { // get screen coordinates vec2 uv = gl_FragCoord.xy / resolution; vec3 normal = worldNormal; // calculate refraction and add to the screen coordinates vec3 refracted = refract(eyeVector, normal, 1.0/ior); uv += refracted.xy; // sample the background texture vec4 tex = texture2D(envMap, uv); vec4 output = tex; // calculate the Fresnel ratio float f = Fresnel(eyeVector, normal); // mix the refraction color and reflection color output.rgb = mix(output.rgb, vec3(1.0), f); gl_FragColor = vec4(output.rgb, 1.0); } 

https://codesandbox.io/embed/multi-side-refraction-step-23-3vdty؟fontsize=14&hidenavigation=1&theme=dark


يبدو بالفعل أفضل بكثير ، ولكن هناك شيء آخر مفقود ... بالضبط ، لا نرى الجزء الخلفي من الكائن الشفاف. دعونا إصلاحه!


الخطوة 3: الانكسار متعدد الأطراف


بالنظر إلى ما تعلمناه بالفعل عن الانكسارات والانعكاسات ، يمكن فهم أن الضوء يمكن أن يعمل ذهابًا وإيابًا داخل كائن عدة مرات قبل مغادرته.
لتحقيق النتيجة الصحيحة ، من وجهة نظر مادية ، سيتعين علينا تتبع كل شعاع ، لكن لسوء الحظ ، فإن هذه الحسابات ثقيلة للغاية بحيث لا يمكن تصورها في الوقت الفعلي. بدلاً من ذلك ، سأريك كيفية استخدام التقريب لإظهار الحواف المخفية للماس على الأقل.
سنحتاج إلى خريطة عادية ووجوه أمامية وخلفية في شظية واحدة. نظرًا لأنه يتعذر علينا تصور كلا الجانبين في نفس الوقت ، نحتاج أولاً إلى الحصول على الحواف الخلفية كملمس.



قم بإنشاء ShaderMaterial جديد كما في الخطوة الأولى ، ولكن الآن سنقوم gl_FragColor الخريطة العادية في gl_FragColor .


 varying vec3 worldNormal; void main() { gl_FragColor = vec4(worldNormal, 1.0); } 

بعد ذلك ، نقوم بتحديث دورة التصور ونضيف معالجة الوجوه الخلفية.


 this.backfaceFbo = new THREE.WebGLRenderTarget(width, height); ... render() { requestAnimationFrame( this.render ); this.renderer.clear(); // render background to fbo this.renderer.setRenderTarget(this.envFbo); this.renderer.render( this.scene, this.orthoCamera ); // render diamond back faces to fbo this.mesh.material = this.backfaceMaterial; this.renderer.setRenderTarget(this.backfaceFbo); this.renderer.clearDepth(); this.renderer.render( this.scene, this.camera ); // render background to screen this.renderer.setRenderTarget(null); this.renderer.render( this.scene, this.orthoCamera ); this.renderer.clearDepth(); // render diamond with refraction material to screen this.mesh.material = this.refractionMaterial; this.renderer.render( this.scene, this.camera ); }; 

الآن نستخدم نسيجًا مع المواد الطبيعية في المادة.


 vec3 backfaceNormal = texture2D(backfaceMap, uv).rgb; 

وأخيرًا ، تتوافق الأوضاع الطبيعية للوجوه الأمامية والخلفية.


 float a = 0.33; vec3 normal = worldNormal * (1.0 - a) - backfaceNormal * a; 

في هذه المعادلة ، a هي مجرد كمية عددية توضح عدد الحالات الطبيعية للحواف الزائدة التي يجب استخدامها.


https://codesandbox.io/embed/multi-side-refraction-step-33-ljnqj؟fontsize=14&hidenavigation=1&theme=dark


اتضح! جميع جوانب الماس مرئية فقط بمساعدة الانعكاسات والانكسار ، والتي أضفناها إلى المادة.


قيود


كما شرحت بالفعل ، ليس من الممكن للغاية الحصول على مواد شفافة في الوقت الحقيقي صحيحة جسديًا باستخدام هذه الطريقة. مشكلة أخرى هي تصور العديد من الأشياء الزجاجية التي تواجه بعضها البعض. نظرًا لأننا نعرض الخلفية مرة واحدة فقط ، فلن يعمل على رؤيتها من خلال سلسلة من الكائنات. وأخيرًا ، لن تعمل الانعكاسات في مجال الرؤية التي أوضحتها هنا بشكل طبيعي على حدود الشاشة ، لأن الأشعة يمكن أن تنكسر بقيم تتجاوز حدود الطائرة ، ولن نكون قادرين على التقاط هذه الحالات عند تحويل المشهد إلى نسيج.


بالطبع ، هناك طرق للتغلب على هذه القيود ، ولكن لن تكون جميعها حلول WebGL رائعة.


أتمنى أن تستمتع بهذا البرنامج التعليمي وأن تتعلم شيئًا جديدًا. أتساءل ماذا ستفعل به الآن! اسمحوا لي أن أعرف على تويتر . ولا تتردد في أن تسألني عن كل شيء!

Source: https://habr.com/ru/post/ar477418/


All Articles