README (README.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
by Patricio Gonzalez Vivo and Jen Lowe
This is a gentle step-by-step guide through the abstract and complex universe of Fragment Shaders.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Contents
* Getting started
* What is a shader?
* “Hello world!”
* Uniforms
* Running your shader
* Algorithmic drawing
* Shaping functions
* Colors
* Shapes
* Matrices
* Patterns
* Generative designs
* Random
* Noise
* Cellular noise
* Fractional brownian motion
* Fractals
* Image processing
* Textures
* Image operations
* Kernel convolutions
* Filters
* Other effects
* Simulation
* Pingpong
* Conway
* Ripples
* Water color
* Reaction diffusion
* 3D graphics
* Lights
* Normal-maps
* Bump-maps
* Ray marching
* Environmental-maps (spherical and cube)
* Reflect and refract
* Appendix: Other ways to use this book
* How can I navigate this book offline?
* How to run the examples on a Raspberry Pi?
* How to print this book?
* How can I collaborate?
* An introduction for those coming from JS by Nicolas Barradeau
* Glossary
About the Authors
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentina) is a New York based artist and developer. He explores interstitial spaces between organic and synthetic, analog and digital, individual and collective. In his work he uses code as an expressive language with the intention of developing a better together.
Patricio studied and practiced psychotherapy and expressive art therapy. He holds an MFA in Design & Technology from Parsons The New School, where he now teaches. Currently he works as a Graphic Engineer at Mapzen making openSource mapping tools.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">WebSite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe is an independent data scientist and data communicator at Datatelling where she brings together people + numbers + words. She teaches in SVA's Design for Social Innovation program, cofounded the School for Poetic Computation, taught Math for Artists at NYU ITP, researched at the Spatial Information Design Lab at Columbia University, and contributed ideas at the White House Office of Science and Technology Policy. She's spoken at SXSW and Eyeo. Her work has been covered by The New York Times and Fast Company. Her research, writing, and speaking explore the promises and implications of data and technology in society. She has a B.S. in Applied Math and a Master's in Information Science. Often oppositional, she's always on the side of love.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">WebSite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Acknowledgements
Thanks Scott Murray for the inspiration and advice.
Thanks Kenichi Yoneda (Kynd), Nicolas Barradeau, Karim Naaji for contributing with support, good ideas and code.
Thanks Kenichi Yoneda (Kynd) and Sawako for the Japanese translation (日本語訳)
Thanks Tong Li and Yi Zhang for the Chinese translation (中文版)
Thanks Jae Hyun Yoo and June Kim for the Korean translation (한국어)
Thanks Nahuel Coppero (Necsoft) for the Spanish translation (español)
Thanks Raphaela Protásio and Lucas Mendonça for the Portuguese translation (portugues)
Thanks Nicolas Barradeau and Karim Naaji for the French translation (français)
Thanks Andrea Rovescalli for the Italian translation (italiano)
Thanks Michael Tischer for the German translation (deutsch)
Thanks Sergey Karchevsky for the Russian translation (russian)
Thanks Vu Phuong Hoang for the Vietnamese translation (Tiếng Việt)
Thanks Wojciech Pachowiak for the Polish translation (polski)
Thanks Manoylov Andriy for the Ukrainian translation (український переклад)
Thanks Batuhan Dev for the Turkish translation (Türkçe)
Thanks Andy Stanton for fixing and improving the pdf/epub export pipeline
Thanks to everyone who has believed in this project and contributed with fixes or donations.
Get new chapters
Sign up for the news letter or follow it on Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
LICENSE
Copyright (c) Patricio Gonzalez Vivo, 2015 - http://patriciogonzalezvivo.com/
All rights reserved.
---
Docker Compose.Yml (docker-compose.yml)
version: '3.5'
services:
web:
image: php:7.0-apache
volumes:
- .:/var/www/html/
ports:
- "8000:80"
---
README Ch (README-ch.md)
<canvas id="custom" class="canvas" data-fragment-url="examples/moon.frag" data-textures="examples/images/moon-texture.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
by Patricio Gonzalez Vivo
这是一本关于 Fragment Shaders(片段着色器)的入门指南,它将一步一步地带你领略其中的纷繁与抽象。
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
关于翻译
这本书是 Patricio 的 the Book of Shaders 的中文翻译。我们希望借此将 Shader 这个有趣有益的工具介绍给更多国人。能力所限,不免有误,如有翻译不当,也请多多指出。
感谢 Patricio 对我们的翻译的信任和支持。
目录
* 关于这本书
* 开始
* 什么是片段着色器(Fragment Shader)?
* “Hello world!”
* Uniforms值
* 运行你的 shader
* 用算法绘画
* 造型函数
* 颜色
* 形状
* 矩阵
* 图案
* 生成设计
* 随机
* 噪声
* 网格噪声
* 分形布朗运动
* 分形
* 图像处理:
* 纹理
* 图像处理
* 卷积核
* 滤镜
* 其他效果
* 模拟
* 乒乓
* Conway生命游戏
* 水波
* 水彩
* 反应扩散
* 3D 图形
* 灯光
* 法线贴图
* 凹凸贴图
* 光线跟踪(Ray marching)
* 环境贴图 (spherical and cube)
* 折射和反射
* 附录: 其他阅读本书的方式
* 如何离线阅读此书?
* 如何在树莓派上运行示例程序?
* 如何打印这本书
* 我怎样共创这本书
* 给那些从JS语言过来的人的介绍 by Nicolas Barradeau
* 词汇表
关于作者
Patricio Gonzalez Vivo (1982, 布宜诺斯艾利斯, 阿根廷) 是一个驻地纽约的艺术家、开发者。他致力于探索有机和人造、模拟信号和数字信号、个体和整体之间的空间。他用代码这种富有表达力的语言来创造更美好的事物。
Patricio 研习和实践精神疗法(psychotherapy)和表达性艺术治疗(expressive art therapy)。他毕业于 Parsons 的设计与科技专业,且目前执教于此。目前他作为 Mapzen 的图形开发工程师制作一些开源的 mapping tool。
<div class="header"><a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
关于译者
* tornote 翻译 00-05 及第 11 章。
* Artrustee 翻译 06-10 章。
后续章节作者仍在撰写中,如果感兴趣可以在 github 上查看部分后续章节代码。
致谢
感谢我的妻子 Jen Lowe, 感谢她无条件的支持、帮助以及编辑此书。
感谢 Scott Murray 给予的启发和建议。
感谢 Kenichi Yoneda (Kynd) 和 Sawako 的 日文版翻译(日本語訳)
感谢 Tong Li 和 Yi Zhang 的 中文版(Chinese) 翻译。
感谢 Jae Hyun Yoo 的 韩文版 (한국어) 翻译。
感谢 Nahuel Coppero (Necsoft) 的 西班牙语(español) 翻译。
感谢 Manoylov Andriy 的 乌克兰语(українська) 翻译。
感谢 Karim Naaji 在代码和想法上的支持和贡献。
感谢所有相信这个项目的人contributed with fixes 以及大家的捐赠.
获取新的章节
注册以获得最新章节, 或 在 Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord 上关注:
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README De (README-de.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
von Patricio Gonzalez Vivo und Jen Lowe
Dies ist eine behutsame Schritt-für-Schritt-Einführung in die komplexe und vielfach abstrakte Welt der Fragment Shader.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Inhalt
* Jetzt geht’s los
* Was ist ein Shader?
* “Hallo Welt!”
* Uniforms
* Ausführung Deiner Shader
* Algorithmisches Zeichnen
* Formgebende Funktionen
* Farben
* Formen
* Matrizen
* Muster
* Generative Designs
* Zufall
* Rauschen
* Zelluläres Rauschen
* Gebrochene Brownsche Bewegung
* Fraktale
* Bildverarbeitung
* Texturen
* Bildbearbeitungsfunktionen
* Kernel Verwindungen
* Filter
* Weitere Effekte
* Simulationen
* Pingpong
* Conway
* Wellen
* Wasserfarben
* Reaktionsausbreitung
* 3D Grafiken
* Licht
* Normal Mapping
* Bump-Mapping
* Ray Marching
* Environmental-Maps (sphärisch und kubisch)
* Reflektionen und Ablenkungen
* Anhang: Weitere Wege zur Nutzung dieses Buches
* Wie kann ich dieses Buch offline lesen?
* Wie lasse ich die Beispielprogramme auf einem Raspberry Pi ablaufen?
* Wie drucke ich dieses Buch aus?
* Wie kann ich zu diesem Buch beitragen?
* Eine Einführung für alle, die von JavaScript kommen von Nicolas Barradeau (Englisch)
* Glossar (Englisch)
Über die Autoren
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentinien) ist ein Künstler und Entwickler, der in New York lebt. Er erforscht die Räume zwischen organisch und synthetisch, analog und digital, einzeln und zusammen. In seinen Arbeiten nutzt er Programmcode als Ausdrucksform, um das Zusammenwirken von Menschen zu verbessern.
Patricio hat Psychologie studiert, außerdem kunstorientiertes Handeln (die sogenannte Expressive Arts Therapy). Er hat einen MFA-Abschluss in Design und Technologie von der Parsons New School For Design, wo er auch unterrichtet. Zur Zeit arbeitet Patricio als Grafikingenieur bei der Firma Mapzen und entwickelt dort Open-Source-Werkzeuge für die Computer-Kartographie.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">Webseite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe ist eine unabhängige Datenwissenschaftlerin und Datenkommunikatorin bei der Firma Datatelling, wo sie Menschen, Zahlen und Sprache zusammenführt. Sie unterrichtet an der SVA das Fach Design for Social Innovation, hat die Schule für „Poetic Computation“ mitbegründet, Mathematik für Künstler an der New Yorker ITP-Universität unterrichtet, Forschungen am „Spatial Information Design Lab“ der Columbia Universität durchgeführt und Beiträge für das „White House Office of Science and Technology“ geliefert, das den US-Präsidenten in Fragen des technischen Fortschritts berät. Als Sprecherin ist Jen auf Konferenzen wie der SXSW und der Eyeo aufgetreten. Von ihren Arbeiten hat unter anderem die New York Times, sowie das Magazin FastCompany berichtet. Ihre Forschungsarbeiten, Publikationen und Vorträge kreisen um die Versprechungen und Folgen von Daten und Technologien für die gesellschaftliche Entwicklung. Sie hat einen Bachelor in angewandter Mathematik und einen Master in Informatik. Obwohl man angesichts dieser Biographie vielleicht etwas anderes vermuten könnte, schlägt sich Jen immer auf die Seite der Liebe.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">Webseite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Danksagungen
Dank an Scott Murray für die Inspirationen und Ratschläge.
Dank an Kenichi Yoneda (Kynd), Nicolas Barradeau und Karim Naaji für ihre Unterstützung, Anregungen und Programmcode.
Dank an Kenichi Yoneda (Kynd) und Sawako für die japanische Übersetzung des Textes (日本語訳)
Dank an Tong Li und Yi Zhang für die chinesische Übersetzung des Textes (中文版)
Dank an Jae Hyun Yoo für die koreanische Übersetzung des Textes (한국어)
Dank an Nahuel Coppero (Necsoft) für die
spanische Übersetzung des Textes (español)
Dank an Nicolas Barradeau und Karim Naaji für die französische Übersetzung des Textes (français)
Dank an Andrea Rovescalli für die italienische Übersetzung des Textes (italiano)
Dank an Michael Tischer für die deutsche Übersetzung des Textes
Dank an Manoylov Andriy für die ukrainische Übersetzung des Textes (українська)
Und natürlich Danke an alle, die an dieses Projekt geglaubt, dafür gespendet oder durch Hinweise und Korrekturen daran mitgewirkt haben.
Hol Dir die neuen Kapitel
Melde Dich für den Newsletter an oder folge uns auf Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Es (README-es.md)
<canvas id="custom" class="canvas" data-fragment-url="examples/moon.frag" data-textures="examples/images/moon-texture.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
por Patricio Gonzalez Vivo
Esta es una guía paso a paso a través del abstracto y complejo universo de los Fragment Shaders.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Contenidos
* Comenzando
* ¿Qué es un shader?
* “Hola Mundo!”
* Uniforms
* Ejecutando tu shader
* Dibujando con algoritmos
* Funciones de forma
* Colores
* Formas
* Matrices
* Patrones
* Diseños generativos
* Random
* Noise
* Movimiento browniano
* Fractales
* Procesamiento de imágenes
* Texturas
* Operaciones de Imagen
* Convoluciones de Kernel
* Filtros
* Otros efectos
* Simulación
* Pingpong
* Conway
* Ripples
* Water color
* Reaction diffusion
* Gráficos 3D
* Luces
* Normal-maps
* Bump-maps
* Ray marching
* Environmental-maps (esféricas y cúbicas)
* Reflección y refracción
* Apéndice: Otras formas de usar este libro
* ¿Cómo puedo ver el libro offline?
* ¿Cómo puedo correr los ejemplos en una Raspberry Pi?
* ¿Cómo imprimir este libro?
* Glosario
Acerca del autor
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentina) es un artista y desarrollador que actualmente reside en New York. Explora los espacios que convergen entre lo orgánico y lo sintético, lo analógico y lo digital, lo individual y lo colectivo. En su trabajo utiliza el código como un lenguaje expresivo con la intención de lograr algo superador.
Patricio estudió y ejerció psicoterapia y terapia de artes expresivas. Tiene un MFA en diseño y tecnología de la Parsons The New School, donde hoy da clases. Actualmente el trabaja como Graphic Engineer en Mapzen, creando herramientas open source para trabajar con mapas.
<div class="header"><a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Agradecimientos
Gracias a mi esposa Jen Lowe, por su apoyo incondicional, su ayuda y el tiempo dedicado editando este libro.
Gracias a Scott Murray por inspirarme y aconsejarme.
Gracias a Kenichi Yoneda (Kynd) y a Sawako por la Traducción al japonés (日本語訳).
Gracias a Tong Li y a Yi Zhangpor la Traducción al chino (中文版).
Gracias a Jae Hyun Yoo por la Traducción al coreano (한국어).
Gracias a Manoylov Andriy por la traducción al ucraniano (українська)
Gracias a Karim Naaji por su contribución, su apoyo, su código y sus buenas ideas.
Gracias a todos los que creyeron en este proyecto y contribuyeron con sus aportes o donaciones.
¿Cómo conseguir los nuevos capítulos?
Suscribirse al newsletter o seguirnos en Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Fr (README-fr.md)
<canvas id="custom" class="canvas" data-fragment-url="examples/moon.frag" data-textures="examples/images/moon-texture.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
par Patricio Gonzalez Vivo et Jen Lowe
Ceci est un guide étape-par-étape à travers le monde abstrait et complexe des Fragment Shaders.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Contenu
* Pour commencer
* Qu'est ce qu'un shader?
* “Hello world!”
* Les uniforms
* Exécuter votre shader
* Le dessin algorithmique
* Les fonctions
* Les couleurs
* Les formes
* Les matrices
* Les motifs
* Le design génératif
* L'aléatoire
* Le bruit
* Le bruit cellulaire
* Fractional Brownian Motion
* Les Fractals
* Traitement d'image:
* Textures
* Convolution
* Filtres
* Effets
* Simulation
* Pingpong
* Conway
* Ondes
* Aquarelle
* Réaction-Diffusion
* 3D graphics
* Lumières
* Normales
* Environment-maps (spherical and cube)
* Bump
* Ray marching
* Réflection & réfraction
* Annexes : Autres manières d'utiliser ce livre
* Comment puis-je consulter ce livre hors-ligne ?
* Comment exécuter les exemples sur un Raspberry Pi ?
* Comment imprimer ce livre ?
* Comment puis-je contribuer à ce livre?
* Une introduction pour ceux qui viennent de JS de Nicolas Barradeau
A propos de l'auteur
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentine) est un artiste et développeur installé à New York. Il explore les espaces interstitiels entre l'organique et le synthétique, l'analogique et le numérique, l'individuel et le collectif. Dans son travail, il utilise le code comme un langage expressif avec l'intention de développer un mieux-vivre ensemble.
Patricio a étudié et a pratiqué la psychothérapie et l'art-thérapie. Il détient un MFA en Design & Technologie de Parsons The New School, où il enseigne maintenant. Actuellement il travaille comme ingénieur graphique pour Mapzen, travaillant sur des outils cartographiques openSource.
<div class="header"><a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Remerciements
Merci à ma femme Jen Lowe, pour son support sans faille, son aide et son temps passé à la révision de ce livre.
Merci à Scott Murray pour l'inspiration et les conseils.
Merci à Kenichi Yoneda (Kynd) et Sawako pour la traduction (日本語訳) japonaise
Merci à Tong Li et Yi Zhang pour la traduction (中文版) chinoise
Merci à Jae Hyun Yoo pour la traduction (한국어) coréenne
Merci à Manoylov Andriy pour la traduction (українська) l'ukrainien
Merci à Karim Naaji qui a contribué par son support, ses bonnes idées et son code.
Merci à tous ceux qui ont cru en ce projet et à ceux qui ont contributé aux corrections ou qui ont fait des dons.
De nouveaux chapitres
Abonnez-vous à la newsletter ou suivez-nous sur Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Id (README-id.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
oleh Patricio Gonzalez Vivo dan Jen Lowe
Panduan langkah demi langkah untuk melalui semesta Fragment Shaders yang abstrak dan kompleks.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Konten
* Memulai
* Apa itu shader?
* “Hello world!”
* Uniforms
* Menjalankan shader
* Menggambar secara algoritmik
* Funsi Membentuk
* Warna
* Bentuk
* Matriks
* Pola
* Desain generatif
* Acak
* Noise
* Noise seluler
* Gerak Pecahan Brownian
* Fraktal
* Pemrosesan gambar
* Tekstur
* Operasi Gambar
* Konvolusi Kernel
* Filter
* Efek lainnya
* Simulasi
* Pingpong
* Conway
* Riak
* Warna air
* Reaksi difusi
* Grafik 3D
* Cahaya
* Peta-normal
* Peta benjolan
* Ray berbaris
* Peta lingkungan (bulat dan kubus)
* Merefleksikan dan membiaskan
* Lampiran: Cara lain untuk menggunakan buku ini
* Bagaimana saya mendapatkan buku ini secara offline?
* Bagaimana cara menjalankan contoh di Raspberry Pi?
* Bagaimana mencetak buku ini?
* Bagaimana saya bisa berkolaburasi?
* An introduction for those coming from JS oleh Nicolas Barradeau
Tentang Penulis
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentina) adalah seorang artis dan developer yang tinggal di New York. Dia menjelajahi ruang interstisial antara organik dan sintetis, analog dan digital, individu dan kolektif. Dalam karyanya ia menggunakan kode sebagai bahasa ekspresif dengan tujuan untuk mengembangkan bersama menjadi lebih baik.
Patricio mempelajari dan mempraktikkan psikoterapi dan terapi seni ekspresif. Dia memegang gelar MFA dalam Desain & Teknologi dari Parsons The New School, tempat dia sekarang mengajar. Saat ini dia bekerja sebagai Graphic Engineer di Mapzen membuat alat pemetaan openSource.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">WebSite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe adalah seorang ilmuwan data independen dan komunikator data di Datatelling tempat dia mengumpulkan orang + angka + kata. Dia mengajar di program SVA's Design for Social Innovation, mendirikan School for Poetic Computation, mengajar Matematika untuk Seniman di NYU ITP, meneliti di Lab Desain Informasi Spasial di Universitas Columbia, dan menyumbangkan ide di White House Office of Science and Technology Policy. Dia berbicara di SXSW dan Eyeo. Karyanya telah diliput oleh The New York Times dan Fast Company. Penelitian, penulisan, dan ceramahnya mengeksplorasi janji dan implikasi data dan teknologi di masyarakat. Dia memiliki gelar B.S. dalam Matematika Terapan dan Magister Ilmu Informasi. Seringkali berlawanan, dia selalu berada di sisi cinta.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">WebSite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Ucapan Terima Kasih
Terima kasih kepada Scott Murray untuk nasihat dan inspirasi.
Terima kasih kepada Kenichi Yoneda (Kynd), Nicolas Barradeau, Karim Naaji untuk kontribusi dengan dukungan, ide bagus dan kode.
Terima kasih kepada Kenichi Yoneda (Kynd) dan Sawako untuk terjemahan Bahasa Jepang (日本語訳)
Terima kasih kepada Tong Li dan Yi Zhang untuk terjemahan Bahasa China (中文版)
Terima kasih kepada Jae Hyun Yoo untuk terjemahan Bahasa Korea (한국어)
Terima kasih kepada Nahuel Coppero (Necsoft) untuk terjemahan Bahasa Spanyol (español)
Terima kasih kepada Raphaela Protásio dan Lucas Mendonça untuk terjemahan terjemahan Bahasa Portugis
Terima kasih kepada Nicolas Barradeau and Karim Naaji for the French translation (français)
Terima kasih kepada Andrea Rovescalli untuk terjemahan Bahasa Italia (italiano)
Terima kasih kepada Michael Tischer untuk terjemahan Bahasa Jerman (deutsch)
Terima kasih kepada Sergey Karchevsky untuk terjemahan Bahasa Rusia (russian)
Terima kasih kepada Andy Stanton untuk perbaikan dan improvisasi the pdf/epub export pipeline
Terima kasih kepada Naufal Adriansyah untuk terjemahan Bahasa Indonesia
Terima kasih kepada Manoylov Andriy untuk terjemahan Bahasa Ukraina (українська)
Terima kasih kepada semua orang yang telah percaya pada proyek ini dan telah berkontribusi dalam perbaikan atau donasi.
Dapatkan bagian baru
Daftar untuk surat berita atau follow di Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README It (README-it.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
di Patricio Gonzalez Vivo e Jen Lowe
Questa è una guida passo passo attraverso l'universo astratto e complesso dei Fragment Shaders.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Contenuto
* Introduzione
* Che cosa è uno shader?
* "Ciao mondo!"
* Uniforms
* Eseguite il vostro shader
* Disegno algoritmico
* Le funzioni di forma
* Colori
* Figure
* Matrici
* Motivi
* Progettazione generativa
* Random
* Rumore
* Rumore cellulare
* Moto browniano frazionario
* Frattali
* Trattamento delle immagini:
* Textures
* Operazioni d'immagine
* Convoluzioni di Kernel
* Filtri
* Altri effetti
* Simulazione
* Pingpong
* Conway
* Increspature
* Acquerello
* Reazione-diffusione
* Grafica 3D
* Luci
* Normal-maps
* Bump-maps
* Ray marching
* Environmental-maps (sferiche e cubiche)
* Riflesso e rifrazione
* Appendice: Altri modi per utilizzare questo libro
* Come posso consultare questo libro offline?
* Come posso eseguire gli esempi su un Raspberry Pi?
* Come posso stampare questo libro?
* Come posso collaborare a questo libro?
* Un'introduzione per chi proviene da JS di Nicolas Barradeau
A proposito dell'autore
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentina) è un artista e sviluppatore con sede a New York. Esplora lo spazio interstiziale fra organico e sintetico, analogico e digitale, individuale e collettivo. Nel suo lavoro usa il codice come un linguaggio espressivo con l'intenzione di creare un migliore vivere insieme.
Patricio studiò e praticò la psicoterapia e l'arteterapia. Ha conseguito un MFA in Design e Tecnologia alla Parsons The New School dove ora insegna. Attualmente lavora come Ingegnere Grafico alla Mapzen realizzando strumenti cartografici openSource.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">WebSite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe è una scienziata indipendente e comunicatrice di dati alla Datatelling dove riunisce persone + numeri + parole. Insegna alla SVA's Design per il programma di Social Innovation, ha co-fondato la School for Poetic Computation, ha insegnato Matematica per Artisti al NYU ITP, ha fatto della ricerca al Spatial Information Design Lab presso la Columbia University, e ha contribuito con idee alla White House Office of Science and Technology Policy. Ha parlato al SXSW e Eyeo. Il suo lavoro è stato trattato dal The New York Times and Fast Company. La sua ricerca, i suoi scritti e la sue dissertazioni esplorano le promesse e le implicazioni dei dati e della tecnologia nella società. Ha una laurea triennale in Matematica Applicata e una laurea specialistica in Scienze Informatiche. Spesso combattiva, è sempre dalla parte dell'amore.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">WebSite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Ringraziamenti
Grazie a Scott Murray per l'ispirazione e i consigli.
Grazie a Kenichi Yoneda (Kynd), a Nicolas Barradeau, a Karim Naaji per aver contribuito con il loro sostegno, con delle buone idee e con del codice.
Grazie a Kenichi Yoneda (Kynd) e a Sawako per la traduzione giapponese (日本語訳)
Grazie a Tong Li e a Yi Zhang per la traduzione cinese (中文版)
Grazie a Jae Hyun Yoo per la traduzione (한국어) coreana
Grazie a Nahuel Coppero (Necsoft) per la traduzione (español) spagnola
Grazie a Nicolas Barradeau e a Karim Naaji per la traduzione (français) francese
Grazie a Andrea Rovescalli per la traduzione italiana
Grazie a Manoylov Andriy per la traduzione ucraina (українська)
Grazie a tutti coloro i quali hanno creduto in questo progetto e contribuito con correzioni o donazioni.
Come ottenere i nuovi capitoli?
Iscriviti alla newsletter o seguici su Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Jp (README-jp.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
Patricio Gonzalez Vivo、Jen Lowe著
この本はフラグメントシェーダーについてのガイドブックです。難解で複雑なフラグメントシェーダーの世界を、一歩一歩わかりやすくご案内します。
翻訳について
この文書はPatricio Gonzalez VivoのThe Book of Shaderの日本語訳です。
技術的な内容はできる限り正確に伝えるように心がけていますが、その他の部分では言葉を補ったり簡略化したり、表現を変えている部分もあります。英語が得意な方はぜひ原文もお読みください。
フィードバックにはgithubのレポジトリをご利用ください。
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
目次
* 初めの一歩
* シェーダーとは?
* ハロー・ワールド!
* ユニフォーム変数
* シェーダーを使う
* アルゴリズムで絵を描く
* シェイピング関数
* 色について
* 形について
* 二次元行列
* パターン
* Generative designs
* ランダム
* ノイズ
* セルラーノイズ
* Fractional brownian motion
* Fractals
* Image processing:
* Textures
* Image operations
* Kernel convolutions
* Filters
* Others effects
* Simulation
* Pingpong
* Conway
* Ripples
* Water color
* Reaction diffusion
* Voronoi
* 3D graphics
* Lights
* Normal-maps
* Bump-maps
* Ray marching
* Environmental-maps (spherical and cube)
* Reflect and refract
* Appendix: Other ways to use this book
* How can I navigate this book offline?
* How to run the examples on a Raspberry Pi?
* How to print this book?
* Glossary
著者について
Patricio Gonzalez Vivo(1982年、アルゼンチン、ブエノスアイレス生) はニューヨーク在住のアーティスト/開発者です。有機的なものと人工的なもの、アナログとデジタル、個人と集団の狭間を探求し、プログラミングコードを表現手段としてより良い共存のあり方を模索しています。
Patricioは心理療法(psychotherapy)と表現療法(expressive art therapy)に精通しており、パーソンズ美術大学でデザイン&テクノロジーのMFA(Master of Fine Arts - 美術系の修士に相当)を取得しています。現在はMapzenでグラフィックエンジニアとしてオープンソースのマッピングツールの開発に携わっています。
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">WebSite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
訳者について
この人 が翻訳しました。冒険野郎マクガイバーは見たことがありません。一部助っ人の力も借りています。
謝辞
妻のJen Loweはこの本の編集のために時間を割き、献身的にサポートしてくれました。ありがとう。
インスピレーションとアドバイスを与えてくれたScott Murray、日本語訳を担当してくれたKynd、素晴らしいアイデアとコードで貢献してくれたKarim Naaji にも感謝します。
(українська) ウクライナ語の翻訳をしてくれた Manoylov Andriy にも感謝します。
そして最後に、このプロジェクトを応援し改善の手助けをしてくれた方々、寄付をくださったすべての皆様に感謝を述べたいと思います。
アップデートのお知らせ
ニュースレターにサインアップするか Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord をフォローしてください。日本語版の更新は訳者のアカウントでもお知らせします。
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Kr (README-kr.md)
<canvas id="custom" class="canvas" data-fragment-url="examples/moon.frag" data-textures="examples/images/moon-texture.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
by Patricio Gonzalez Vivo
이것은 황량하게 넓고 복잡한 Fragment Shader의 세계를 한단계씩 살펴보는 지침서입니다.
번역에 대하여
이 문서는 Patricio Gonzalez Vivo의 The Book of Shader의 한국어 번역입니다.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
차례
* 이 책에 대하여
* 시작하기 앞서
* 쉐이더란 무엇인가?
* “헬로 월드!”
* 유니폼
* 쉐이더 실행하기
* Algorithmic drawing
* 모양 그리기
* 색에 대해
* 형태에 대해
* 행렬
* 패턴
* Generative designs
* 랜덤
* 노이즈
* 셀룰러 노이즈
* 분수 브라운운동
* 프랙탈
* Image processing:
* Textures
* Image operations
* Kernel convolutions
* Filters
* Others effects
* Simulation
* Pingpong
* Conway
* Ripples
* Water color
* Reaction diffusion
* 3D graphics
* Lights
* Normal-maps
* Bump-maps
* Ray marching
* Environmental-maps (spherical and cube)
* Reflect and refract
* 부록: 이 책을 활용하는 다른 방법들
* 이 책을 오프라인(off-line)으로 보는 방법
* 라즈베리 파이에서 예제를 실행하는 방법
* 이 책을 인쇄하는법
* Collaborator로 기여하기
* JS 사용자분들을 위한 안내서 by Nicolas Barradeau
* 예제 갤러리
* 용어집
저자 소개
Patricio Gonzalez Vivo (1982, 부에노스 아이레스, 아르헨티나) 는 뉴욕 기반의 아티스트 겸 개발자이다. 그는 유기적요소와 인공적요소, 아날로그와 디지털, 단일적요소와 집단적요소 사이를 탐험한다. 그는 사람들과 함께 개발하기위해 코드를 표현요소로 사용해 작업을 한다.
Patricio는 심리치료 및 표현예술을 공부했다. 그는 파슨스대학에서 디자인 및 기술 석사학위를 보유하고 있고 그곳에서 학생을 가르치고 있다. 또한 그는 현재 Mapzen에서 그래픽스 엔지니어로 맵을 만드는 오픈소스툴을 개발중에 있다.
<div class="header"><a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe 은 Datatelling에서 사람+숫자+단어를 한데 모으는 데이터과학자이자 커뮤니케이터다. 그녀는 SVA의 사회혁신 프로그램 설계를 강의하고, 시적연산학교를 공동 설립했으며, NYU ITP에서 아티스트를 위한 수학을 가르쳤고, 컬럼비아 대학교의 공간 정보 디자인 연구소에서 연구했으며, 백악관 과학기술 정책실에서 아이디어를 기여했다. 그녀는 SXSW와 Eyeo에서 연설해왔다. 그녀의 작품은 뉴욕타임즈와 패스트 컴퍼니에서 보도 되었다. 그녀의 연구, 글쓰기 및 연설은 사회에서 데이터와 기술의 약속과 시사점을 탐구한다. 그녀는 응용수학 학사, 정보과학 석사 학위를 가지고 있다. 그녀는 종종 저항적이면서도 항상 사랑의 편에 선다.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">WebSite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
감사의 표시
영감과 격려를 아끼지 않는 Scott Murray에게 감사를 표합니다.
좋은 아이디어와 코드를 제공해주는 Kenichi Yoneda (Kynd), Nicolas Barradeau, Karim Naaji에게 감사를 표합니다.
일본어 번역을 맡고 있는 Kenichi Yoneda (Kynd) 와 Sawako 에게 감사를 표합니다. Japanese translation (日本語訳)
중국어 번역을 맡고 있는 Tong Li 와 Yi Zhang 에게 감사를 표합니다. Chinese translation (中国的翻译)
한국어 번역을 맡고 있는 유재현 과 김준 에게 감사를 표합니다. Korean translation (한국어)
스페인어 번역을 맡고 있는 Nahuel Coppero (Necsoft) 에게 감사를 표합니다. Español translation
포르투갈어 번역을 맡고 있는 Raphaela Protásio 와 Lucas Mendonça 에게 감사를 표합니다. Portugues translation
프랑스어 번역을 맡고 있는 Nicolas Barradeau 와 Karim Naaji 에게 감사를 표합니다. Français translation
이탈리아어 번역을 맡고 있는 Andrea Rovescalli 에게 감사를 표합니다. Italiano translation
독일어 번역을 맡고 있는 Michael Tischer 에게 감사를 표합니다.Deutsch translation
러시아어 번역을 맡고 있는 Sergey Karchevsky 에게 감사를 표합니다. Russian translation
러시아어 번역을 맡고 있는 Manoylov Andriy 에게 감사를 표합니다. Ukrainian translation (українська)
pdf/epub 배포 수정 및 개선을 맡고 있는 Andy Stanton 에게 감사를 표합니다.
이 프로젝트를 격려해주시고 기부해주신 모든 분들께 감사를 드립니다. Contributed with fixes
새로운 챕터 업데이트 받기
뉴스 업데이트를 위해 팔로우 해주세요. follow it on Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Pl (README-pl.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
autorstwa Patricio Gonzalez Vivo i Jen Lowe
Łagodny, krok po kroku przewodnik przez abstrakcyjny i złożony świat Fragment Shaderów.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Spis treści
* Wprowadzenie
* Czym jest shader?
* "Witaj świecie!"
* Uniformy
* Uruchomienie shaderu
* Rysowanie algorytmiczne
* Shaping functions
* Kolory
* Kształty
* Macierze
* Wzorce
* Design generatywny
* Random
* Noise
* Cellular noise
* Fractal Brownian Motion
* Fraktale
* Image processing
* Textures
* Image operations
* Kernel convolutions
* Filters
* Other effects
* Simulation
* Pingpong
* Conway
* Ripples
* Water color
* Reaction diffusion
* 3D graphics
* Lights
* Normal-maps
* Bump-maps
* Ray marching
* Environmental-maps (spherical and cube)
* Reflect and refract
* Dodatek: Inne sposoby korzystania z tej książki
* Jak mogę korzystać z tej książki offline?
* Jak uruchomić przykłady na Raspberry Pi?
* Jak wydrukować tę książkę?
* Jak mogę pomóc?
* Wprowadzenie dla biegłych w JavaScript by Nicolas Barradeau
O autorach
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentyna) jest nowojorskim artystą i developerem. Bada granice między organicznym a syntetycznym, analogowym a cyfrowym, indywidualnym i kolektywnym. W swojej pracy używa kodu jako ekspresywnego języka, z intencją rozwijania współpracy międzyludzkiej.
Patricio studiował i praktykował psychoterapię oraz arteterapię. Otrzymał tytuł magistra sztuk pięknych w Design & Technology od Parsons The New School, gdzie aktualnie uczy. Obecnie pracuje jako inżynier graficzny w Mapzen, gdzie tworzy opensource'owe narzędzia do kartografii komputerowej.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">www</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe jest niezależną data scientist i komunikatorką danych w Datatelling, gdzie łączy ludzi + liczby + słowa. Uczy w ramach programu Design for Social Innovation na SVA, współzałożyła School for Poetic Computation, uczyła matematyki dla artystów w NYU ITP, była badaczką w Spatial Information Design Lab na Uniwersytecie Columbia oraz zgłaszała swoje pomysły do Office of Science and Technology Policy Białego Domu. Przemawiała na SXSW i w Eyeo. Jej pracę opisywały The New York Times i Fast Company. Jej działalność badawcza, pisarska i mównicza eksplorują obietnice oraz implikacje danych i technologii dla społeczeństwa. Obroniła tytuły licencjata matematyki stosowanej oraz magistra informatyki. Często opozycyjna, ale zawsze po stronie miłości.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">www</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Podziękowania
Podziękowania dla Scott Murray za porady i inspirację.
Podziękowania dla Kenichi Yoneda (Kynd), Nicolas Barradeau, Karim Naaji za wsparcie, dobre pomysły i kod.
Podziękowania dla Kenichi Yoneda (Kynd) i Sawako za japońskie tłumaczenie (日本語訳)
Podziękowania dla Tong Li i Yi Zhang za chińskie tłumaczenie (中文版)
Podziękowania dla Jae Hyun Yoo i June Kim za koreańskie tłumaczenie (한국어)
Podziękowania dla Nahuel Coppero (Necsoft) za hiszpańskie tłumaczenie (español)
Podziękowania dla Raphaela Protásio i Lucas Mendonça za portugalskie tłumaczenie (portugues)
Podziękowania dla Nicolas Barradeau i Karim Naaji za francuskie tłumaczenie (français)
Podziękowania dla Andrea Rovescalli za włoskie tłumaczenie (italiano)
Podziękowania dla Michael Tischer za niemieckie tłumaczenie (deutsch)
Podziękowania dla Sergey Karchevsky za rosyjskie tłumaczenie (russian)
Podziękowania dla Vu Phuong Hoang za wietnamskie tłumaczenie (Tiếng Việt)
Podziękowania dla Wojciecha Pachowiaka za polskie tłumaczenie (polski)
Podziękowania dla Manoylov Andriy za ukraińskie tłumaczenie (українська)
Podziękowania dla Andy Stanton za naprawę i usprawnienie funkcji eksportu pdf/epub
Podziękowania dla każdego, kto współtworzy ten projekt poprzez swoje rady, korekty lub finansowe wsparcie.
Zdobądź nowe rozdziały
Zapisz się do newslettera lub obserwuj na Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Pt (README-pt.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
por Patricio Gonzalez Vivo e Jen Lowe
Este é um guia passo-a-passo pelo universo abstrato e complexo de Fragment Shaders.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Conteúdo
* Início
* O que é um shader?
* “Olá mundo!”
* Uniforms
* Executando seu shader
* Desenho algorítmico
* Funções de forma
* Cores
* Formas
* Matrizes
* Padrões
* Design generativo
* Aleatório
* Noise
* Cellular noise
* Movimento browniano fracionário
* Fractais
* Processamento de imagem
* Texturas
* Operações de imagem
* Convoluções do kernel
* Filtros
* Outros efeitos
* Simulação
* Pingpong
* Conway
* Ondulações
* Aquarela
* Reação-Difusão
* Gráficos em 3D
* Luzes
* Normal-maps
* Bump-maps
* Ray marching
* Environmental-maps (esférico e cúbico)
* Reflexão e refração
* Apêndice: Outras formas de usar este livro
* Como eu posso ler este livro offline?
* Como eu posso rodar os exemplos em um Raspberry Pi?
* Como imprimir este livro?
* Como eu posso colaborar?
* Uma introdução para quem é familiarizado com JS por Nicolas Barradeau
Sobre os autores
Patricio Gonzalez Vivo (1982, Buenos Aires, Argentina) é um artista e desenvolvedor residente em New York. Ele explora os espaços intersticiais entre o orgânico e sintético, analógico e digital, individual e coletivo. Em seu trabalho utiliza código como linguagem expressiva com a intenção de desenvolver uma melhor conexão entre as pessoas.
Patricio estudou e praticou psicoterapia e terapia de arte expressiva. Ele possui MFA em Design & Technologia pela Parsons The New School, onde hoje leciona. Atualmente trabalha como Engenheiro Gráfico na Mapzen, criando ferramentas open source de mapeamento.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">WebSite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe é uma cientista de dados independente e comunicadora de dados na Datatelling, onde une pessoas + números + palavras. Ela leciona no SVA's Design for Social Innovation program, co-fundou a School for Poetic Computation, ensinou Math for Artists na NYU ITP, pesquisou no Spatial Information Design Lab na Columbia University, e contribuiu com ideias na White House Office of Science and Technology Policy. Ela discursou no SXSW e Eyeo. Seu trabalho foi coberto pelo The New York Times e Fast Company. Sua pesquisa, escritos, e palestras exploram as promessas e implicações dos dados e da technologia na sociedade. Ela é bacharel em Matemática Aplicada e mestre em Ciência da Informação. Frequentemente oposicionista, ela está sempre do lado do amor.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">WebSite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Acknowledgements
Obrigado Scott Murray pela inspiração e conselhos.
Obrigado Kenichi Yoneda (Kynd), Nicolas Barradeau, Karim Naaji por contribuir com apoio, boas ideias e código.
Obrigado Kenichi Yoneda (Kynd) e Sawako pela tradução em Japonês (日本語訳)
Obrigado Tong Li e Yi Zhang pela tradução em Chinês (中文版)
Obrigado Jae Hyun Yoo pela tradução em Koreano (한국어)
Obrigado Nahuel Coppero (Necsoft) pela tradução em Espanhol (español)
Obrigado Nicolas Barradeau e Karim Naaji pela tradução em Francês (français)
Obrigado Andrea Rovescalli pela tradução em Italiano (italiano)
Obrigado Michael Tischer pela tradução em Alemão (deutsch)
Obrigado Sergey Karchevsky pela tradução em Russo (russian)
Obrigado Manoylov Andriy pela tradução em Ucraniano (українська)
Obrigado Andy Stanton por corrigir e melhorar a pipeline para exportar pdf/epub
Obrigado a todos que acreditaram neste projeto e contribuíram com correções ou doações.
Receba novos capítulos
Assine a newsletter ou nos siga no Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Ru (README-ru.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
Патрицио Гонзалес Виво и Джен Лав
Пошаговое руководство по абстрактной и сложной вселенной фрагментных шейдеров.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Содержание
* Введение
* Что такое шейдер?
* “Hello world!”
* Uniform-переменные
* Запуск шейдера
* Алгоритмическое рисование
* Формообразующие функции
* Цвета
* Фигуры
* Матрицы
* Узоры
* Генеративный дизайн
* Беспорядок
* Шум
* Клеточный шум
* Фрактальное броуновское движение
* Фракталы
* Обработка изображений
* Текстуры
* Операции над изображениями
* Свёртка с ядром
* Фильтры
* Другие эффекты
* Симуляция
* Пинг-понг
* Игра «Жизнь» Конвея
* Рябь
* Вода
* Реакционно-диффузная модель
* 3D-графика
* Освещение
* Карты нормалей
* Карты высот
* Ray marching
* Карты окружения (сферические и кубические)
* Отражение и преломление
* Приложение: Другие варианты использования этой книги
* Как читать книгу оффлайн?
* Как запустить примеры на Raspberry Pi?
* Как напечатать книгу?
* Как принять участие в создании книги?
* Введение для JavaScript-программистов (Николя Баррадо)
* Примеры
Об авторах
Патрицио Гонзалес Виво (1982, Буэнос Айрес, Аргентина) - художник и разработчик, живущий в Нью-Йорке. Он исследует многообразие пространств между природным и рукотворным, аналоговым и цифровым, индивидуальным и коллективным. В своей работе он использует код как средство выразительности, делая этот мир лучше.
Патрицио изучал и практиковал психотерапию и арт-терапию. Он получил степень магистра наук и искусств в области дизайна и технологии в Parsons The New School, где в настоящее время преподаёт. Сейчас он работает инженером по графике в Mapzen, где создаёт картографический инструментарий с открытым исходным кодом.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">Сайт</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Джен Лав собирает воедино людей, числа и слова, работая независимым исследователем данных в Datatelling. Она преподаёт в Нью-Йоркской школе изобразительного искусства по программе социальных инноваций, является соучредителем Школы поэтических коммуникаций, преподавала математику для художников в NYU ITP, занималась исследованиями в Лаборатории пространственного дизайна в Университете Колумбии, вносила предложения в офис Белого дома по науке и технологиям. Выступала на SXSW и Eyeo. Её работы освещались в The New York Times и Fast Company. Её исследования, публикации и доклады затрагивают перспективы и последствия развития технологий обработки данных для общества. Имеет степень бакалавра в области прикладной математики и степень магистра в области информатики.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">Сайт</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Благодарности
Спасибо Скотту Мюррею за вдохновение и советы.
Спасибо Кеничи Йонеде (Kynd), Николя Баррадо и Кариму Нааджи за поддержку, хорошие идеи и код.
Спасибо Кеничи Йонеде (Kynd) и Савако за японский перевод (日本語訳)
Спасибо Тонь Ли и И Жань за китайский перевод (中文版)
Спасибо Ча Хьюн Ю за корейский перевод (한국어)
Спасибо Науэлю Копперо (Necsoft) за испанский перевод (español)
Спасибо Николя Баррадо и Кариму Нааджи за французский перевод (français)
Спасибо Андреа Ровескалли за итальянский перевод (italiano)
Спасибо Майклу Тишеру за немецкий перевод (deutsch)
Спасибо Сергею Карчевскому за русский перевод
Спасибо Андрею Манойлову за украинский перевод
Спасибо всем кто поверил в этот проект и поддержал его исправлениями или пожертвованиями.
Новые параграфы
Чтобы получать оповещение о новых параграфах, подпишитесь на почтовую рассылку или Твиттер / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
README Tr (README-tr.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
Patricio Gonzalez Vivo ve Jen Lowe tarafından
Bu, Fragment Shader'ların soyut ve karmaşık evrenine adım adım, nazik bir rehberdir.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
İçindekiler
* Başlarken
* Shader nedir?
* “Merhaba dünya!”
* Uniform'lar
* Shader'ınızı çalıştırma
* Algoritmik çizim
* Şekillendirme fonksiyonları
* Renkler
* Şekiller
* Matrisler
* Desenler
* Üretken tasarımlar
* Rastgelelik (Random)
* Gürültü (Noise)
* Hücresel gürültü
* Fraktal Brownian hareketi
* Fraktallar
* Görüntü işleme
* Dokular (Textures)
* Görüntü işlemleri
* Kernel konvolüsyonları
* Filtreler
* Diğer efektler
* Simülasyon
* Pingpong
* Conway
* Dalgalanmalar
* Suluboya
* Reaksiyon difüzyonu
* 3D grafikler
* Işıklar
* Normal haritaları
* Kabartma haritaları (Bump-maps)
* Işın yürütme (Ray marching)
* Çevresel haritalar (küresel ve küp)
* Yansıma ve kırılma
* Ek: Bu kitabı kullanmanın diğer yolları
* Bu kitapta çevrimdışı nasıl gezinebilirim?
* Örnekleri bir Raspberry Pi üzerinde nasıl çalıştırırım?
* Bu kitabı nasıl yazdırırım?
* Nasıl katkıda bulunabilirim?
* JS'den gelenler için bir giriş - Nicolas Barradeau tarafından
* Sözlük
Yazarlar Hakkında
Patricio Gonzalez Vivo (1982, Buenos Aires, Arjantin), New York merkezli bir sanatçı ve geliştiricidir. Organik ve sentetik, analog ve dijital, bireysel ve kolektif arasındaki ara alanları keşfeder. Çalışmalarında kodu, birlikte daha iyi bir gelecek geliştirme niyetiyle etkileyici bir dil olarak kullanır.
Patricio psikoterapi ve dışavurumcu sanat terapisi okumuş ve uygulamıştır. Parsons The New School'dan Tasarım ve Teknoloji alanında yüksek lisans derecesine sahiptir ve şu anda orada ders vermektedir. Halen Mapzen'de açık kaynak haritalama araçları yapan bir Grafik Mühendisi olarak çalışmaktadır.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">Web Sitesi</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe, insanları + sayıları + kelimeleri bir araya getirdiği Datatelling'de bağımsız bir veri bilimcisi ve veri iletişimcisidir. SVA'nın Sosyal İnovasyon için Tasarım programında ders vermekte, School for Poetic Computation'ın kurucu ortaklığını yapmış, NYU ITP'de Sanatçılar için Matematik dersi vermiş, Columbia Üniversitesi'ndeki Uzamsal Bilgi Tasarım Laboratuvarı'nda araştırmalar yapmış ve Beyaz Saray Bilim ve Teknoloji Politikası Ofisi'nde fikirlere katkıda bulunmuştur. SXSW ve Eyeo'da konuşmalar yapmıştır. Çalışmaları The New York Times ve Fast Company tarafından ele alınmıştır. Araştırmaları, yazıları ve konuşmaları verinin ve teknolojinin toplumdaki vaatlerini ve etkilerini araştırmaktadır. Uygulamalı Matematik alanında lisans ve Bilgi Bilimi alanında yüksek lisans derecesine sahiptir. Genellikle muhalif olsa da, her zaman sevgiden yanadır.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">Web Sitesi</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Teşekkürler
İlham ve tavsiyeler için Scott Murray'ye teşekkürler.
Destekleri, iyi fikirleri ve kod katkıları için Kenichi Yoneda (Kynd), Nicolas Barradeau, Karim Naaji'ye teşekkürler.
Japonca çeviri (日本語訳) için Kenichi Yoneda (Kynd) ve Sawako'ya teşekkürler.
Çince çeviri (中文版) için Tong Li ve Yi Zhang'a teşekkürler.
Korece çeviri (한국어) için Jae Hyun Yoo ve June Kim'e teşekkürler.
İspanyolca çeviri (español) için Nahuel Coppero (Necsoft)'ya teşekkürler.
Portekizce çeviri (portugues) için Raphaela Protásio ve Lucas Mendonça'ya teşekkürler.
Fransızca çeviri (français) için Nicolas Barradeau ve Karim Naaji'ye teşekkürler.
İtalyanca çeviri (italiano) için Andrea Rovescalli'ye teşekkürler.
Almanca çeviri (deutsch) için Michael Tischer'e teşekkürler.
Rusça çeviri (russian) için Sergey Karchevsky'ye teşekkürler.
Vietnamca çeviri (Tiếng Việt) için Vu Phuong Hoang'a teşekkürler.
Lehçe çeviri (polski) için Wojciech Pachowiak'a teşekkürler.
Ukraynaca çeviri (український переклад) için Manoylov Andriy'ye teşekkürler.
Türkçe çeviri (Türkçe) için Batuhan Dev'e teşekkürler.
PDF/epub dışa aktarma hattını düzelttiği ve geliştirdiği için Andy Stanton'a teşekkürler.
Bu projeye inanan ve düzeltmelerle veya bağışlarla katkıda bulunan herkese teşekkürler.
Yeni bölümleri alın
Haber bültenine kaydolun veya Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord üzerinden takip edin.
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
LİSANS
Telif Hakkı (c) Patricio Gonzalez Vivo, 2015 - http://patriciogonzalezvivo.com/
Tüm hakları saklıdır.
---
README Vi (README-vi.md)
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
The Book of Shaders
Tác giả: Patricio Gonzalez Vivo và Jen Lowe
Quyển sách này sẽ hướng dẫn người đọc khám phá dần vũ trụ trừu tượng và phức tạp của Fragment Shader.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
Nội dung
* Giới thiệu
* Shader là gì?
* “Hello world!”
* Uniform
* Chạy thử shader
* Các thuật toán hình học
* Các hàm số cơ bản (Hàm hình dạng - Shape function)
* Màu sắc
* Hình dạng
* Ma trận
* Mẫu hoa văn (Pattern)
* Các thiết kế ngẫu nhiên
* Ngẫu nhiên
* Nhiễu (Noise)
* Nhiễu mô phỏng tế bào (Cellular noise)
* Chuyển động Brown (Fractional Brownian motion)
* Phân dạng (Fractals)
* Xử lý ảnh
* Ảnh vân bề mặt (Texture)
* Các phép toán xử lý ảnh
* Tích chập (Kernel convolution)
* Bộ lọc (Filter)
* Các hiệu ứng khác
* Mô phỏng
* Bóng bàn (Pingpong)
* Tiến trình sống của Conway
* Gợn sóng
* Màu nước
* Mô hình Phản ứng-Khuếch tán (Reaction-Diffusion)
* Đồ hoạ 3 chiều
* Ánh sáng
* Giả lập chi tiết bề mặt bằng vector pháp tuyến (Normal map)
* Giả lập chi tiết bề mặt từ mặt cong (Bump map)
* Dò tia quang học (Ray marching)
* Ánh xạ từ môi trường bằng mặt cầu hoặc khối lập phương (Environmental map)
* Phản xạ và khúc xạ
* Phụ lục: Các cách khác để đọc và đóng góp cho quyển sách này
* Làm thế nào để đọc quyển sách này mà không cần Internet ?
* Làm thế nào để chạy thử ví dụ trên Raspberry Pi ?
* Làm thế nào để in quyển sách này ?
* Làm thế nào để góp sức cho quyển sách này ?
* Giới thiệu cho người đã biết Javascript từ Nicolas Barradeau
* Chú giải
Tác giả
Patricio Gonzalez Vivo (sinh năm 1982 tại Buenos Aires, Argentina) là artist kiêm developer ở New York。 Anh ấy khám phá sự giao thoa giữa hữu cơ và vô cơ, analog và kỹ thuật số, cá nhân và tập thể. Trong công việc, anh ấy dùng code như một cách đóng góp tạo nên những điều tốt đẹp hơn cho cộng đồng.
Patricio đã học tập và công tác trong ngành tâm lý trị liệu và nghệ thuật biểu cảm. Anh ấy có bằng Thạc sỹ ngành Thiết kế và Công nghệ từ trường Parson The New School, cũng là nơi anh ấy đang giảng dạy. Hiện tại, anh ấy là Kỹ sư đồ hoạ tại Mapzen để phát triển các công cụ cho openSource.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">WebSite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
Jen Lowe là một nhà khoa học dữ liệu độc lập ở Datatelling, nơi mà cô ấy khám phá mối liên hệ giữa con người với chữ số và ngôn từ. Cô ấy hiện đang giảng dạy ở khoa Thiết kế của trường Mỹ thuật New York theo chương trình Social Innovation. Cô ấy đã cùng sáng lập ra School for Poetic Computation, dạy môn Toán chuyên ngành nghệ thuật ở khoa Sau Đại Học của trường New York, công tác tại phòng nghiên cứu Spatial Information Design tại Đại học Columbia và còn đóng góp nhiều ý tưởng cho Văn phòng Nhà Trắng về các chính sách Khoa học công nghệ. Cô ấy đã từng thuyết trình ở SXSW và Eyeo. Sản phẩm của cô ấy đã xuất hiện trên trang bìa của tờ The New York Times và Fast Company. Công việc nghiên cứu, sáng tác và thuyết trình của cô ấy chủ yếu khai thác đề tài về tiềm năng và ý nghĩa của việc áp dụng các thành tựu của ngành dữ liệu và công nghệ cho cộng đồng. Cô ấy có bằng Cử nhân khoa học ngành Toán ứng dụng và bằng Thạc sỹ ngành Khoa học thông tin. Nhưng cô ấy cũng là một người hay mơ mộng.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">WebSite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
Lời cảm ơn
Cảm ơn Scott Murray vì niềm cảm hứng và những lời khuyên.
Cảm ơn Kenichi Yoneda (Kynd), Nicolas Barradeau, Karim Naaji vì đã hỗ trợ cả về ý tưởng lẫn code.
Cảm ơn Kenichi Yoneda (Kynd) và Sawako vì Bản dịch tiếng Nhật (日本語訳)
Cảm ơn Tong Li và Yi Zhang vì Bản dịch tiếng Trung (中文版)
Cảm ơn Jae Hyun Yoo vì Bản dịch tiếng Hàn (한국어)
Cảm ơn Nahuel Coppero (Necsoft) vì Bản dịch tiếng Tây Ban Nha (español)
Cảm ơn Raphaela Protásio và Lucas Mendonça vì Bản dịch tiếng Bồ Đào Nha (portugues)
Cảm ơn Nicolas Barradeau và Karim Naaji vì Bản dịch tiếng Pháp (français)
Cảm ơn Andrea Rovescalli vì Bản dịch tiếng Ý (italiano)
Cảm ơn Michael Tischer vì Bản dịch tiếng Đức (deutsch)
Cảm ơn Sergey Karchevsky vì Bản dịch tiếng Nga (russian)
Cảm ơn Vu Phuong Hoang và Minh-Phuc Bui vì Bản dịch tiếng Việt
Cảm ơn Manoylov Andriy vì Bản dịch tiếng Ukraina (українська)
Cảm ơn Andy Stanton vì đã sửa lỗi và cải tiến cách export quyển sách ra định dạng pdf/epub
Cảm ơn tất cả mọi người đã tin tưởng, cùng sửa lỗi và quyên góp cho dự án này.
Thông báo khi có chương mới
Đăng ký nhận thư định kỳ hoặc theo dõi trên Twitter / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / Discord
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
---
.Travis.Yml (.travis.yml)
language: python
python:
- 2.7
install:
- sudo apt-get install cmake xorg-dev libglu1-mesa-dev texlive-xetex texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended pandoc imagemagick
- # Install glfw
- git clone https://github.com/glfw/glfw.git ~/glfw
- cd ~/glfw
- cmake .
- make
- sudo make install
- cd -
- # Install glslViewer
- git clone http://github.com/patriciogonzalezvivo/glslViewer ~/glslViewer
- cd ~/glslViewer
- make
- sudo make install
- cd -
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sleep 3 # give xvfb some time to start
- rm -rf 12 13 14 15 16 17 18 # ugly ...
script:
- make && [ -e book.pdf ]
---
00/README (00/README.md)
Introduction
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
The images above were made in different ways. The first one was made by Van Gogh's hand applying layer over layer of paint. It took him hours. The second was produced in seconds by the combination of four matrices of pixels: one for cyan, one for magenta, one for yellow and one for black. The key difference is that the second image is produced in a non-serial way (that means not step-by-step, but all at the same time).
This book is about the revolutionary computational technique, fragment shaders, that is taking digitally generated images to the next level. You can think of it as the equivalent of Gutenberg's press for graphics.
Fragment shaders give you total control over the pixels rendered on the screen at a super fast speed. This is why they're used in all sort of cases, from video filters on cellphones to incredible 3D video games.
In the following chapters you will discover how incredibly fast and powerful this technique is and how to apply it to your professional and personal work.
Who is this book for?
This book is written for creative coders, game developers and engineers who have coding experience, a basic knowledge of linear algebra and trigonometry, and who want to take their work to an exciting new level of graphical quality. (If you want to learn how to code, I highly recommend you start with Processing and come back later when you are comfortable with it.)
This book will teach you how to use and integrate shaders into your projects, improving their performance and graphical quality. Because GLSL (OpenGL Shading Language) shaders compile and run on a variety of platforms, you will be able to apply what you learn here to any environment that uses OpenGL, OpenGL ES or WebGL. In other words, you will be able to apply and use your knowledge with Processing sketches, openFrameworks applications, Cinder interactive installations, Three.js websites or iOS/Android games.
What does this book cover?
This book will focus on the use of GLSL pixel shaders. First we'll define what shaders are; then we'll learn how to make procedural shapes, patterns, textures and animations with them. You'll learn the foundations of shading language and apply it to more useful scenarios such as: image processing (image operations, matrix convolutions, blurs, color filters, lookup tables and other effects) and simulations (Conway's game of life, Gray-Scott's reaction-diffusion, water ripples, watercolor effects, Voronoi cells, etc.). Towards the end of the book we'll see a set of advanced techniques based on Ray Marching.
There are interactive examples for you to play with in every chapter. When you change the code, you will see the changes immediately. The concepts can be abstract and confusing, so the interactive examples are essential to helping you learn the material. The faster you put the concepts into motion the easier the learning process will be.
What this book doesn't cover:
This is not* an OpenGL or webGL book. OpenGL/webGL is a bigger subject than GLSL or fragment shaders. To learn more about OpenGL/webGL I recommend taking a look at: OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (also known as the red book) or WebGL: Up and Running
This is not* a math book. Although we will cover a number of algorithms and techniques that rely on an understanding of algebra and trigonometry, we will not explain them in detail. For questions regarding the math I recommend keeping one of the following books nearby: 3rd Edition of Mathematics for 3D Game Programming and computer Graphics or 2nd Edition of Essential Mathematics for Games and Interactive Applications.
What do you need to start?
Not much! If you have a modern browser that can do WebGL (like Chrome, Firefox or Safari) and a internet connection, click the “Next” Chapter button at the end of this page to get started.
Alternatively, based on what you have or what you need from this book you can:
- Make an off-line version of this book
- Run the examples on a Raspberry Pi without a browser
- Make a PDF of the book to print
- Check the GitHub repository of this book to help resolve issues and share code.
---
00/README Ch (00/README-ch.md)
关于这本书
引言
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
上面两幅图是由不同的方式制成的。第一张是梵高一层一层徒手画出来的,需要花费些时间。第二张则是用 4 个像素矩阵分秒钟生成的:一个青色,一个品红,一个黄色,和一个黑色矩阵。关键的区别在于第二张图是用非序列方式实现的(即不是一步一步实现,而是多个同时进行)。
这本书是关于这个革命性的计算机技术,片段着色器(fragment shaders),它将数字生成的图像提到了新的层次。你可以把它看做当年的古腾堡印刷术。
Fragment shaders(片段着色器)可以让你控制像素在屏幕上的快速渲染。这就是它在各种场合被广泛使用的原因,从手机的视频滤镜到酷炫的的3D视频游戏。
在接下来的章节你会发现这项技术是多么难以置信地快速和强大,还有如何将它应用到专业的和个人的作品中。
这本书是为谁而写的?
这本书是写给有代码经验和线性代数、三角学的基本知识的创意编程者、游戏开发者和工程师的,还有那些想要提升他们的作品的图像质量到一个令人激动的新层次的人。(如果你想要学习编程,我强烈推荐你先学习Processing,等你玩起来processing,再回来看这个)。
这本书会教你如何使用 shaders(着色器)并把它整合进你的项目里,以提升作品的表现力和图形质量。因为GLSL(OpenGL的绘制语言)的shaders 在很多平台都可以编译和运行,你将可以把在这里学的运用到任何使用OpenGL, OpenGL ES 和 WebGL 的环境中。也就是说,你将可以把学到的知识应用到Processing,openFrameworks,Cinder,Three.js和iOS/Android游戏中。
这本书包含哪些内容?
这本书专门关于 GLSL pixel shaders。首先我们会给出shaders的定义;然后我们会学习如何制作程序里的形状,图案,材质,和与之相关的动画。你将会学到基础的着色语言并把它们应用到有用的情景中,比如:图像处理(图像运算,矩阵卷积,模糊,颜色滤镜,查找表及其他效果)和模拟(Conway 的生命游戏,Gray-Scott 反应扩散,水波,水彩效果,Voronoi 细胞等等)。到书的最后我们将看到一系列基于光线跟踪(Ray Marching)的进阶技术。
每章都会有可以玩的交互的例子。当你改动代码的时候,你会立刻看到这些变化。一些概念可能会晦涩难懂,而这些可交互的例子会对你学习这些材料非常有益。你越快把这些代码付诸实践,你学习的过程就会越容易。
这本书里不包括的内容有:
* 这不是一本 openGL 或 webGL 的书。OpenGL/webGL 是一个比GLSL 或 fragment shaders 更大的主题。如果你想要学习 openGL/webGL 推荐看: OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (也被叫做红宝书) 或 WebGL: Up and Running
。
* 这不是一本数学书。虽然我们会涉及到很多关于线代和三角学的算法和技术,但我们不会详细解释它。关于数学的问题我推荐手边备一本:3rd Edition of Mathematics for 3D Game Programming and computer Graphics 或 2nd Edition of Essential Mathematics for Games and Interactive Applications。
开始学习需要什么准备?
没什么。如果你有可以运行 WebGL 的浏览器(像Chrome,Firefox或Safari)和网络,点击页面底端的“下一章”按钮就可以开始了。
此外,基于你有的条件或需求你可以:
* 用github仓库来帮助解决问题和分享代码
---
00/README De (00/README-de.md)
Einleitung
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Die oben abgebildeten Grafiken wurden auf verschiedene Weise erstellt. Die linke Abbildung stammt aus den Händen des Malers Van Gogh, der die Farben in stundenlanger Arbeit Schicht für Schicht mit einem Pinsel aufgetragen hat. Die rechte Abbildung wurde dagegen innerhalb von wenigen Sekunden mit Hilfe von vier Pixelmatrizen erzeugt: eine für cyan, eine für magenta, eine für gelb und eine für schwarz. Der entscheidende Unterschied ist, dass das zweite Bild nicht seriell erstellt wurde, also nicht Schritt für Schritt, sondern parallel, alle Punkte zur gleichen Zeit.
Dieses Buch handelt von der Rechentechnik mit dem Namen Fragment-Shader, die die digitale Erzeugung von Bildern revolutioniert und zu neuen Höhen geführt hat. Man kann ihre Erfindung ein wenig vergleichen mit dem Schritt von der manuellen Vervielfältigung einzelner Grafiken und Dokumente hin zur massenhaften Replikation durch Gutenbergs Druckerpresse.
Fragment-Shader ermöglichen die vollständige Kontrolle über alle Bildpunkte, die als Grafik auf dem Bildschirm erscheinen. Und das mit ungeheurer Geschwindigkeit. Deshalb wird diese Technik mittlerweile in vielen Bereichen der Computergrafik angewandt, von Videofiltern auf Smartphones bis hin zu beeindruckenden 3D-Videospielen.
Die folgenden Kapitel zeigen Dir, wie unglaublich schnell und leistungsfähig diese Technik ist und wie Du sie im Rahmen von privaten und beruflichen Projekten einsetzen kannst.
Für wen ist dieses Buch geeignet?
Dieses Buch wendet sich an kreative Programmierer, Spieleentwickler und Ingenieure, die bereits Programmiererfahrung und grundlegende Kenntnisse in den Bereichen linearen Algebra und Trigonometrie haben. (Falls Du erst noch programmieren lernen möchtest, empfehle ich Dir, mit Processing zu beginnen und anschließend mit diesem Buch fortzufahren.)
Dieses Buch zeigt Dir, wie Du Shader in Deinen Projekten einsetzen kannst und wie Du die Qualität und die Geschwindigkeit von Shadern verbesserst. GLSL-Shader (GLSL steht für „OpenGL Shading Language“) lassen sich auf einer Vielzahl von Hardwareplattformen und Betriebssystemen kompilieren und ausführen. Dadurch kannst Du das erlernte Wissen in jeder Umgebung einsetzen, die OpenGL, OpenGL ES oder WebGL benutzt.
In anderen Worten, Du kannst Dein Know-how u.a. beim Malen mit Processing, bei Anwendungen für openFrameworks, interaktiven Installationen mit Cinder, Webseiten mit Three.js oder bei Spielen für iOS und Android nutzen.
Welchen Aspekten widmet sich dieses Buch?
Dieses Buch behandelt hauptsächlich den Umgang mit GLSL-Pixel-Shadern. Zunächst erklären wir, was Shader sind, dann wenden wir uns der algorithmischen Erzeugung von Formen, Mustern, Texturen und Animationen zu. Du lernst die Grundlagen der Programmiersprache für OpenGL-Shader kennen und erfährst, wie man sie für konkrete Zwecke nutzt. Dazu zählt die Bildbearbeitung (Bildmanipulationen, Matrizenoperationen, Blurfilter, Farbfilter und weitere Effekte), sowie Simulationen (Conways Game of Life, Reaktion und Diffusion von Chemikalien nach Gray-Scott, Erzeugung von Wasserwellen, die Nachbildung des Malens mit Wasserfarben, Erzeugung von Voronoi-Zellen und mehr). Gegen Ende vom Buch lernst Du mehrere fortgeschrittene Techniken kennen, die mithilfe einer Technik namens Ray-Marching beeindruckende 2D-Grafiken aus 3D-Daten generiert werden.
In jedem Kapitel gibt es interaktive Beispiele, mit denen Du vieles ausprobieren kannst. Sobald du etwas am Programmcode änderst, erscheinen die daraus resultierenden Veränderungen an der erzeugten Grafik sofort. Die vorgestellten Konzepte sind teilweise abstrakt und auf den ersten Blick vielleicht ein wenig verwirrend. Aber mithilfe der interaktiven Beispiele kannst Du den Lernstoff leicht nachvollziehen. Je mehr Du ausprobierst, desto einfacher wird Dir das Lernen fallen.
Was dieses Buch nicht behandelt:
Dies ist kein* Buch über OpenGL oder WebGL. OpenGL und WebGL sind ein umfassenderes Thema als GLSL- oder Fragment-Shader. Wenn Du mehr über OpenGL oder WebGL lernen möchtest, empfehle ich Dir die folgenden Materialien: OpenGL Einführung (Englisch), Die achte Ausgabe des „OpenGL Programming Guide“ (Englisch) (auch bekannt als das „Red Book“) oder „WebGL: Up and Running“ (Englisch)
Das vorliegende Werk ist außerdem kein* Mathematik-Buch. Obwohl wir bei vielen Techniken und Algorithmen auf Algebra und Trigonometrie zurückgreifen, werden die mathematischen Grundlagen nicht an jeder Stelle vollständig in allen Details erklärt. Bei Fragen dazu empfehle ich Dir eines der folgenden Bücher: Dritte Ausgabe von „Mathematics for 3D Game Programming and Computer Graphics“ (Englisch) oder Zweite Ausgabe von „Essential Mathematics for Games and Interactive Applications“ (Englisch).
Was benötigst Du, um loszulegen?
Nicht viel! Wenn Du auf Deinem Rechner, Smartphone oder Tablet einen modernen Webbrowser hast, der WebGL unterstützt (etwa Chrome, Firefox oder Safari), und eine Internetverbindung besteht, dann drücke einfach auf die Schaltfläche für das nächste Kapitel am Ende dieser Seite.
Alternativ kannst Du auch:
- Eine Offline-Fassung von diesem Buch erstellen
- Die Beispielprogramme aus diesem Buch direkt auf einem RaspberryPi ausführen (ohne Internet-Browser)
- Eine druckbare PDF-Datei mit diesem Buch erzeugen
- Die Github-Seite von diesem Buch nutzen, um Fehler zu melden und Programmcode mit anderen Lesern zu teilen.
---
00/README Es (00/README-es.md)
Introducción
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Las imágenes que aparecen arriba fueron creadas de diferentes formas. La primera fue hecha por la mano de Van Gogh, aplicando capa por capa la pintura. Le tomó horas pintarla. La segunda fue producida en segundos mezclando cuatro matrices de pixeles: una para el cyan, otra para el magenta, otra para el amarillo y otra para el negro. La diferencia clave entre ambas es que la segunda imagen fue hecha de una manera no serializada (es decir que el proceso no fue paso a paso, sino de manera simultánea).
Este libro trata sobre una técnica computacional revolucionaria, que lleva a las imágenes generadas digitalmente a un nuevo nivel, los fragment shaders. Esta revolución es comparable a lo que fue en su momento la imprenta de Gutemberg para la gráfica.
Los fragment shaders te dan un control total sobre los pixeles renderizados en la pantalla, a una super velocidad. Este es el motivo por el que se usa en todo tipo de casos, desde filtros de video, hasta increíbles videojuegos en 3D.
En los próximos capítulos descubrirás cuán increíblemente rápida y poderosa es esta técnica, y cómo incorporarla a tu trabajo personal o profesional.
¿Para quién es este libro?
Este libro está escrito para creative coders, desarrolladores de videojuegos e ingenieros que tengan alguna experiencia con la programación, un conocimiento básico de álgebra lineal y trigonometría, y que quieran llevar su trabajo a un nuevo y emocionante nivel gráfico. (Si te interesa aprender a programar, te recomiendo comenzar con Processing y volver cuando te sientas cómodo).
Este libro te enseñará a integrar shaders en tus proyectos, mejorando la performance y la calidad gráfica. Como los shaders GLSL (OpenGL Shading Language) se pueden compilar y correr en diferentes plataformas, podrás aplicar lo que aprendas aquí en cualquier entorno que use OpenGL, OpenGL ES o WebGL. En otras palabras podrás utilizarlo en sketches de Processing, aplicaciones de openFrameworks, instalaciones interactivas con Cinder, incluso en web o en juegos iOS/Android con Three.js.
¿Qué temas se tratan en este libro?
Este libro se enfoca en el uso de los pixel shaders GLSL. Primero definiremos qué son los shaders; luego aprenderemos a crear formas procedurales, patrones, texturas y animaciones con ellos. Aprenderás los fundamentos básicos del lenguaje y cómo aplicarlos en escenarios más útiles cómo: el procesamiento de imágenes (operaciones de imagen, convoluciones de matrices, desenfocado, filtros de color, lookup tables y otros efectos) y simulaciones (El juego de la vida de Conway, la reacción-difusión de Gray-Scott, ondas de agua, efecto de acuarela, celdas de Voronoi, etc). Hacia el final del libro veremos un conjunto de técnicas avanzadas basadas en ray marching.
Habrá ejemplos interactivos en cada uno de los capítulos para que puedas jugar con ellos. Cuando modifiques el código, podrás ver al instante los cambios reflejados en la pantalla. Los conceptos expuestos pueden ser abstractos y confusos, por lo que los ejemplos interactivos son esenciales para comprender el material. Cuanto más rápido pongamos manos a la obra en el código, mejor lo entenderemos.
Que cosas no vas a encontrar en este libro:
Este no es* un libro sobre OpenGL o webGL. OpenGL/webGL son temas mucho más grandes que GLSL o el uso fragment shaders. Si estás interesado en aprender openGL/webGL te recomiendo mirar: OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (también conocido como el libro rojo) o WebGL: Up and Running
Este no es* un libro de matemáticas. A pesar de que cubriremos un número de algoritmos y técnicas que están íntimimante relacionados con el álgebra y la trigonometría, no las explicaremos en profundidad. Si tienes alguna duda con respecto a la matemática empleada en este libro te recomiendo tener cerca alguno de estos libros: 3rd Edition of Mathematics for 3D Game Programming and computer Graphics o 2nd Edition of Essential Mathematics for Games and Interactive Applications.
¿Qué necesito para comenzar?
¡No mucho! Si tienes algún navegador que cuente con WebGL (como por ejemplo Chrome, Firefox o Safari) y una conexión a internet, solo necesitas hacer clic en "Siguiente" para poder comenzar.
Alternativamente, dependiendo cuales sean tus necesidades puedes:
- Crear una versión offline del libro
- Correr los ejemplos en una Raspberry Pi sin un navegador
- Crear un PDF del libro para imprimir.
- Usar el repositorio on-line y ayudarnos a resolver problemas y compartir código.
---
00/README Fr (00/README-fr.md)
Introduction
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Les images ci-dessus ont été créees de différentes manières.
La première est un tableau de Van Gogh, réalisée en appliquant des couches de peinture successives.
Cela lui a pris des jours, voire des semaines.
La seconde est réalisée en temps réel en combinant 4 matrices de pixels ; une pour le cyan, une pour le magenta, une pour le jaune et une pour le noir.
La différence principale entre ces deux images (hormis le medium), est que la seconde n'est pas sérielle ; elle n'est pas créée par étapes successives mais d'un bloc grâce à un shader.
Ce livre parle d'une technique, les shaders, qui permet à l'imagerie numérique de s'affranchir de la plupart des contraintes.
On peut comparer l'avènement des shaders à l'avancée qu'a représenté la presse de Guntenberg à son époque.
Les shaders donnent un contrôle total sur les pixels de l'écran à une vitesse très élevée.
C'est pourquoi ils sont utilisés dans une grande variété de domaines allant des filtres photo et vidéo, aux téléphones mobiles jusqu'aux jeux vidéos 3D.
Au fil des chapitres, vous découvrirez à quel point les shaders sont efficaces et comment vous pourrez vous en servir pour vos projets personnels et/ou professionnels.
A qui s'adresse ce livre ?
Ce livre est écrit pour les creative coders, les développeurs de jeux, les ingénieurs et les curieux ayant une expérience avec le code, des connaissances basiques en algèbre linéaire et en trigonométrie et désireux d'améliorer la qualité graphique de leurs productions.
Si vous n'avez aucune connaissance en programmation, nous vous recommandons vivement de commencer par apprendre le langage Processing puis de revenir à ce livre lorsque vous vous sentirez plus à l'aise.
Ce livre vous montrera comment utiliser et intégrer des shaders dans vos projets, comment améliorer leurs performances et leur rendu graphique.
Du fait que les shaders GLSL (OpenGL Shading Language) peuvent être compilés sur diverses plateformes, il vous sera possible de réutiliser ce que vous aurez appris ici dans n'importe quel environnement utilisant OpenGL, OpenGL ES ou WebGL. Autrement dit, vous pourrez réutiliser vos connaissances sur des plateformes comme Processing, openFrameworks, Cinder, Three.js ou iOS et Android.
Que contient ce livre ?
Ce livre se concentre sur l'utilisation des shaders GLSL.
Premièrement, nous définirons ce que sont les shaders; puis nous apprendrons à dessiner des formes, des textures et des motifs de manière procédurale.
Vous apprendrez les bases du langage de shading avant de les utiliser sur des cas concrets comme le traitement d'image (transformations, convolution, flou, filtres et autres effets), les simulations (le jeu de la vie de Conway, la réaction diffusion de Gray-Scott, les ondulations sur un plan d'eau, les cellules de Voronoi, etc.). Enfin nous aborderons les techniques avancées basées sur le Ray Marching.
Chaque chapitre comporte des exemples interactifs, n'hésitez pas à jouer avec! Lorsque vous éditez le code des exemples, le shader est recompilé et - s'il est valide - vous verrez le résultat instantanément. Les concepts paraîssent souvent abstraits et peuvent laisser perplexe, les exemples sont donc là pour vous aider à mieux comprendre ce qui se passe. Nous sommes partisans de l'apprentissage par l'exemple, n'hésitez pas à vous approprier les extraits de codes, quitte à les casser en les modifiants (plusieurs fois s'il le faut).
Ce que ce livre n'est pas :
Ce n'est pas* un livre sur openGL ou webGL. OpenGL/webGL sont des sujets plus profonds que GLSL ou les shaders. Si vous voulez en savoir plus, les liens suivants vous aideront : OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (alias "the red book") ou WebGL: Up and Running
Ce n'est pas* un livre de maths. Même si le livre contient un certain nombre de notions et d'algorithmes nécessitant des connaissances en algèbre et en trigonométrie, nous ne rentrerons pas dans le détail. Pour toute question relative aux maths, vous pouvez consulter un de ces livres et le garder près de vous : 3rd Edition of Mathematics for 3D Game Programming and computer Graphics ou 2nd Edition of Essential Mathematics for Games and Interactive Applications.
Bien démarrer
Si vous avez un navigateur récent (comme Chrome, Firefox ou Safari) et une connexion internet, il vous suffira de cliquer sur "Next" en bas de page pour commencer l'aventure.
Cela étant et selon ce que vous voulez faire de ce livre, vous pouvez :
- Créer une copie locale de ce livre pour le consulter hors-ligne
- Lancer les exemples directement sur Raspberry Pi, sans navigateur
- Créer un PDF du livre pour l'imprimer
- Consulter le dépôt GitHub du livre pour y contribuer ou partager du code.
---
00/README Id (00/README-id.md)
Pengenalan
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Gambar di atas dibuat dengan cara yang berbeda. Yang pertama dibuat oleh tangan Van Gogh dengan mengaplikasikan lapisan di atas lapisan cat. Ini menghabiskan waktu berjam-jam. Yang kedua dibuat dalam waktu sedetik oleh kombinasi 4 matriks dari piksel: satu untuk cyan, satu untuk magenta, satu untuk kuning dan satu untuk hitam. Kunci perbedaannya adalah gambar kedua diproduksi dengan cara non-serial (yang artinya bukan langkah demi langkah, tapi semua dalam waktu yang sama).
Buku ini membahas tentang teknik komputasi revolusioner, shader fragment, yang membawa gambar yang dihasilkan secara digital ke level berikutnya. Anda bisa menganggapnya setara dengan Gutenberg Press untuk grafis.
Shader fragment (fragment shader) memberi kendali penuh atas piksel yang ditampilkan di layar dengan kecepatan super cepat. Inilah sebabnya mengapa mereka digunakan dalam semua jenis kasus, dari filter video di ponsel hingga video game 3D yang luar biasa.
Dalam bab-bab berikut anda akan menemukan betapa luar biasa cepat dan kuatnya teknik ini dan bagaimana menerapkannya pada pekerjaan profesional dan pribadi Anda.
Untuk siapakah buku ini?
Buku ini ditulis untuk coders kreatif, developer dan insinyur game yang memiliki pengalaman coding, pengetahuan dasar tentang aljabar linier dan trigonometri, dan yang ingin membawa pekerjaan mereka ke tingkat kualitas grafis baru yang menarik. (Jika Anda ingin mempelajari cara membuat kode, saya sangat menyarankan Anda memulai dengan Processing dan kembali lagi nanti jika Anda sudah merasa nyaman dengannya.)
Buku ini akan mengajarkan anda cara menggunakan dan mengintegrasikan shader ke proyek anda, mengimprovisasi performanya dan kualitas grafis. Karena GLSL (OpenGL Shading Language) shader dapat dikompilasi dan berjalan di berbagai platform, anda dapat menerapkan apa yang dipelajari di lingkungan lain yang menggunakan OpenGL, OpenGL ES atau WebGL. Dalam kata lain, anda dapat menerapkan dan menggunakan pengetahuan anda dengan sketsa Processing, aplikasi openFrameworks, interaktif Cinder Three.js website atau permaianan iOS/Android.
Apa yang diajarkan buku ini?
Buku ini akan berfokus pada penggunaan shader piksel GLSL. Pertama kita akan mendefinisikan apa itu shader; kemudian kita akan belajar bagaimana membuat bentuk, pola, tekstur dan animasi prosedural dengannya. Anda akan mempelajari dasar-dasar bahasa bayangan dan menerapkannya pada skenario yang lebih berguna seperti: pemrosesan gambar (operasi gambar, konvolusi matriks, blur, filter warna, tabel pencarian, dan efek lainnya) dan simulasi (permainan hidup Conway, Gray-Scott's reaksi-difusi, riak air, efek cat air, sel Voronoi, dll.). Menjelang akhir buku kita akan melihat seperangkat teknik lanjutan berdasarkan Ray Marching
Ada contoh interaktif untuk Anda mainkan di setiap bab. Saat Anda mengubah kode, Anda akan segera melihat perubahannya. Konsepnya bisa abstrak dan membingungkan, jadi contoh interaktif sangat penting untuk membantu Anda mempelajari materi. Semakin cepat Anda menerapkan konsep, semakin mudah proses pembelajarannya.
Apa yang tidak diajarkan buku ini:
Ini bukan* buku OpenGL atau WebGL. OpenGL/webGL adalah subyek yang lebih besar dari GLSL atau fragment shader. Untuk mempelajari lebih lanjut tentang OpenGL/WebGL saya merekomendasikan melihat: OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (yang juga dikenal sebagai Red Book) atau WebGL: Up and Running
Ini bukan* buku matematika. Meskipun kita akan membahas nomor algoritma dan teknik yang mengandalkan pemahaman aljabar linier dan trigonometri, kita tidak akan menjelaskannya secara detail. Untuk pertanyaan mengenai matematika, saya merekomendasikan menyimpan salah satu dari buku: 3rd Edition of Mathematics for 3D Game Programming and computer Graphics atau 2nd Edition of Essential Mathematics for Games and Interactive Applications.
Apa yang dibutuhkan untuk memulai?
Tidak banyak! Jika anda memiliki browser modern yang dapat melakukan WebGL (seperti Chrome, Firefox atau Safari) dan koneksi internet, klik tombol "Next" di akhir halaman untuk memulai.
Sebagai alternatif, berdasarkan apa yang anda miliki atau apa yang Anda butuhkan dari buku ini, Anda dapat:
- Membuat versi offline dari buku ini
- Menjalankan contoh di Raspberry Pi tanpa browser
- Cek repositori Github menyelesaikan masalah dan berbagi kode.
---
00/README It (00/README-it.md)
Introduzione
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Le immagini qui sopra sono state realizzate in modi diversi. La prima è stata dipinta a mano da Van Gogh, aggiungendo strati di pittura uno dopo l'altro. Gli ci vollero ore. La seconda è stata prodotta in qualche secondo dalla combinazione di quattro matrici di pixel: una per il ciano, una per il magenta, una per il giallo e una per il nero. La differenza principale è che la seconda immagine è prodotta in modo non seriale (cioè non passo-passo, ma tutta allo stesso tempo).
Questo libro tratta della tecnica di calcolo rivoluzionario, i fragment shaders, che sta portando a un livello successivo le immagini generate digitalmente. Lo si può pensare come l'equivalente della stampa di Gutenberg per la grafica.
I fragment shaders danno un controllo totale sui pixel, che sono resi sullo schermo a una super velocità. Questo è il motivo per cui sono utilizzati in molteplici applicazioni, dai filtri video sui cellulari ad incredibili videogiochi 3D.
Nei capitoli seguenti scopriremo quanto veloce e potente sia questa tecnica e come poterla applicare al vostro lavoro professionale e personale.
Per chi è questo libro?
Questo libro è scritto per programmatori creativi, sviluppatori di giochi e ingegneri che hanno esperienza di programmazione, una conoscenza di base di algebra lineare e di trigonometria, e che vogliono portare il loro lavoro ad un livello di qualità grafica ancora più emozionante. (Se si vuole imparare a programmare, vi raccomando di iniziare con Processing e tornare più tardi, quando avrete maggiore dimestichezza.)
Questo libro vi insegnerà come utilizzare e integrare shader nei vostri progetti, migliorando le loro prestazioni e la qualità grafica. Poiché gli GLSL (OpenGL Shading Language) shaders compilano e si eseguono su una grande varietà di piattaforme, si potrà applicare ciò che si impara qui a qualsiasi framework che utilizza OpenGL, OpenGL ES o WebGL. In altre parole, si sarà in grado di applicare e utilizzare la propria conoscenza negli sketches di Processing, nelle applicazioni di openFrameworks, nelle installazioni interattive di Cinder, nei siti web con Three.js o nei giochi iOS / Android.
Di che cosa tratta questo libro?
Questo libro si concentrerà su l'uso dei pixel shaders GLSL. Per prima cosa definiremo cosa sono gli shaders; poi impareremo come fare, grazie a loro, forme procedurali, motivi, texture e animazioni. Imparerete le basi del linguaggio shading ed ad applicarlo a scenari più utili, quali: l'elaborazione di immagini (operazioni di immagine, convoluzioni di matrici, sfocature, filtri colorati, tabelle di associazione e altri effetti) e simulazioni (il gioco della vita di Conway, la reazione-diffusione di Gray-Scott, le increspature d'acqua, gli effetti acquerello, le celle di Voronoi, etc.). Verso la fine del libro vedremo un insieme di tecniche avanzate basate sul Ray Marching.
In ogni capitolo, ci sono degli esempi interattivi per imparare divertendosi. Quando si modifica il codice, vedrete immediatamente sullo schermo i cambiamenti. I concetti possono essere astratti e confusi, perciò gli esempi interattivi sono essenziali per aiutarvi a capire gli argomenti trattati. Più velocemente i concetti si mettono in pratica e più facilmente si imparerà.
Di cosa non tratta questo libro:
Questo non è * un libro su OpenGL o WebGL. OpenGL / WebGL è un soggetto più vasto di GLSL o dei fragment shaders. Per ulteriori informazioni su OpenGL / WebGL vi consiglio di dare un'occhiata a: OpenGL Introduzione, l'ottava edizione della guida sulla programmazione OpenGL (noto anche come il libro rosso) o WebGL: Up and Running.
Questo non è* un libro di matematica. Anche se ci occuperemo di una serie di algoritmi e di tecniche che si basano su una comprensione dell'algebra e della trigonometria, non spiegheremo tutto in dettaglio. Per domande riguardanti la matematica vi consiglio di tenere vicino uno dei seguenti libri: La terza edizione di Matematica per la programmazione di giochi 3D e la computer Grafica o La seconda edizione di Matematica Essenziale per Giochi e Applicazioni Interattive.
Cosa ti serve per iniziare?
Non tanto! Se si dispone di un browser moderno che può far girare WebGL (come Chrome, Firefox o Safari) e una connessione Internet, fai clic sul bottone "Next" alla fine di questa pagina per iniziare.
In alternativa, in base a ciò di cui avete bisogno, è possibile:
- Creare versione offline di questo libro
- Eseguire gli esempi su un Raspberry Pi senza navigatore
- Fare un PDF del libro da stampare
- Utilizzare la repository on-line per aiutare a risolvere i problemi e per condividere il codice.
---
00/README Jp (00/README-jp.md)
イントロダクション
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
上の2枚の画像は全く異なる方法で作られたものです。1枚目の画像はゴッホが時間をかけて絵の具を塗り重ねて描いたものです。2枚目はシアン、マゼンタ、イエロー、ブラックの4色の網点の組み合わせでほんの短い時間で生み出されました。2枚目の画像は手順を重ねるのではなく、すべての点が同時に描かれているという大きな違いがあります。
この本はフラグメントシェーダーという、デジタル映像を次の次元へと誘う革命的な計算処理技術についての本です。フラグメントシェーダーは印刷にとってのグーテンベルグの活版技術に匹敵すると考えることもできます。
フラグメントシェーダーを使うと画面に表示されるピクセルをとても高速に、かつ自由にコントロールすることができます。そのためこの技術はスマートフォンで映像にフィルターをかけることから、驚くようなクオリティの3Dゲームまで、様々な用途に使われています。
この後につづく章ではフラグメントシェーダーがどれだけ高速でパワフルな技術かについて、またシェーダーを仕事や個人的なプロジェクトで使う方法について説明します。
想定される読者
この本はプログラムの経験と基本的な線形代数や三角関数の知識があり、ワンランク上のグラフィックを目指すクリエイティブコーダー、ゲームデベロッパーなどの開発者に向けて書かれています。もしコーディングについて学びたい場合はProcessingをまず勉強して戻ってくることをお勧めします。
この本ではフラグメントシェーダーをプロジェクトに組み込む方法、パフォーマンスやグラフィックのクオリティを改善する方法を学ぶことができます。GLSL (OpenGL Shading Language, OpenGLシェーダー言語)は様々なプラットフォーム上でコンパイルして走らせることができるので、ここで学習したことはOpenGL、OpenGL ESまたはWebGLが使えるのできるどんな環境でも生かすことができます。
たとえば、Processingのスケッチ、 openFrameworksで作ったアプリケーション、 Cinderで作ったインスタレーション、 Three.jsを用いたウェブサイトやiOS/Androidのゲームなどが対象です。
この本で取り上げること
この本では主にGLSLのフラグメントシェーダーについて説明します。シェーダーとは何かの定義から始まり、プログラムを用いて形や模様、質感や動きを作る方法を学びます。シェーダー言語の基礎とともに、畳み込み、ぼかし、カラーフィルター、ルックアップテーブルなどの画像処理技術、コンウェイのライフゲーム、グレイ=スコットの反応拡散系、水の波紋、水彩、ボロノイ図のようなシミュレーションなど、より実践的に活用する方法についても学習します。さらにレイマーチングに基づく高度なテクニックにも触れます。
どの章にも実際に動かせるサンプルがついています。サンプルのコードを書き換えれば結果をその場で見ることもできます。難解なコンセプトも、実際に試してみることでよりスムーズに理解できるでしょう。
この本で取り上げないこと
* この本はOpenGLやWebGLの本ではありません。OpenGL、WebGLはGLSLやフラグメントシェーダーを含むより広範なトピックです。もしOpenGL、WebGLについてもっと学びたい場合は下記のWebページや本をお勧めします。
- OpenGL Introduction
- the 8th edition of the OpenGL Programming Guide (赤本)
- WebGL: Up and Running
* この本は数学の本ではありません。代数や三角関数が必要なアルゴリズムやテクニックもとりあげますが、数学についての詳しい説明は省略します。数学的な概念について知りたい場合は下記の本を手元に置いておくと良いでしょう。
- 3rd Edition of Mathematics for 3D Game Programming and computer Graphics
- 2nd Edition of Essential Mathematics for Games and Interactive Applications.
必要なもの
Chrome、FirefoxやSafariなどWebGLの使える今時のブラウザーとインターネットさえあれば始められます。ページの最後にある「Next」をクリックして先に進みましょう。
もし必要な場合は下記もご覧ください。(訳注:いまのところ英語のみです。そのうち翻訳します)
- Make an off-line version of this book
- Run the examples on a Raspberry Pi without a browser
- Make a PDF of the book to print
- Use the on-line repository to help resolve issues and share code.
---
00/README Kr (00/README-kr.md)
Introduction
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
위에 두 이미지는 다른 방법을 통해 만들어졌다. 첫번째것은 반 고흐가 직접 레이어 위에 레이어를 쌓는 방식으로 만들어졌다. 제법 시간이 걸렸을것이다. 두번째것은 몇초 안되는 시간안에 픽셀들의 행렬연산 4개를 통해 만들어 졌다: 한개는 cyan색, 다른 한개는 magenta색, 또 다른 한개는 yellow, 그리고 마지막것은 black. 중요한점은 두번째 이미지는 그림을 부분마다 따로 그려나가게 아니라 그림의 다른 모든 부분이 한번에 그려졌다는 것이다.
이 책은, fragment shaders, 라는 디지털 그림 제작과정을 다음 레벨로 이끌어낼 혁신적인 컴퓨팅 기술에 대한 것이다. 바로 그래픽 인더스트리의 구텐베르크 인쇄술인것이다.
Fragment shader는 매우 빠른 속도로 스크린에 렌더되는 픽셀들을 완전히 컨트롤 할수 있는 능력을 준다. 핸드폰의 비디오 필터나, AAA 3D 비디오 같은 미디어에서 쓰이는 이유도 이것때문이다.
앞으로 맞이할 챕터들은 독자가 이 강력하고 바른 기술을 스스로 습득하고, 자신의 작업에 적용할수 있는 길을 보여줄것이다.
누구를 위한 책인가?
이 책은 삼각함수와 선형대수학에 대한 기본이해가 있고 작업에 있어 그래픽 요소를 극대화 시키려는 creative coder, 게임 개발자, 그래픽 엔지니어들을 위한 책이다. (만약 본인이 코딩입문자라면 이 링크를 보고, Processing 코딩이 익숙해졌을때쯤 다시 돌아와 이책을 보길 권한다.)
이책은 독자들에게 쉐이더를 프로젝트에 어떻게 쓰는지와, 이를 통한 퍼포먼스 향상과 퀄리티를 높이는 방법들을 보여줄것이다. GLSL (OpenGL Shading Language) 쉐이더들은 OpenGL, OpenGL ES 혹은 WebGL을 가진 환경에서 컴파일되고 실행될것이다. 한마디로, 이것에 대한 기술을 Processing 스켓치파일들, openFrameworks 어플들, Cinder 설치미술들, Three.js 웹사이트들 또는 iOS/Android 게임들에 적용할수 있다는 것이다.
어떤 내용들을 다루나?
이책은 GLSL pixel shader를 메인으로 다룬다. 먼저, 쉐이더가 무엇인지 알아보고, 절차적(procedural) 모양, 패턴, 텍스쳐, 애니메이션등에 응용해볼것이다. 쉐이딩 랭기지의 기본에 대해 다지게 되고, 여러 분야에 적용해볼수 있도록 유도하는데 예를들면: 이미지 처리(이미지 여산, 메트릭스 회선, blurs, color filters, 룩업테이블과 기타 효과들)와 시뮬레이션 (Conway's game of life, Gray-Scott's reaction-diffusion, water ripples, 물감 효과, Voronoi cells등등)이 그 좋은 예이다. 책의 막바지에는 Ray Marching과 같은 고급기술도 소개를 한다.
각 챕터는 인터엑티브한 예제들로 구성되어 있다. 예제들의 코드를 변경하면, 바로 그에 대한 결과를 볼수 있다. 컨셉자체가 추상적이거나 난해할수도 있기에, 이런 인터엑티브 예제가 이해하는데 큰 도움이 될거라고 믿는다. 컨셉에 대한 예제를 많이 가지고 놀아볼수록 그에 대한 이해역시 빨리 될거라고 생각한다.
이책이 다루지 않는 부분:
* 이책은 openGL이나 webGL서적이 아니다. openGL/webGL은 GLSL이나 fragment shader보다 훨씬 더 큰 주제이다. 그런것들에 대해 좀더 싶히 공부하고 싶다면: OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (빨간책이라고도 알려진) 이나 WebGL: Up and Running 등을 추천한다.
* 이책은 수학색이 아니라. 물론 여러 종류의 알고리즘과 대수학과, 삼각함수에 기댄 수학수식이 많이 이용되지만, 그런것들에 대해 자세히 설명하지는 않을 것이다. 수학에 대한 질문은 : 3rd Edition of Mathematics for 3D Game Programming and computer Graphics 를 보거나, 2nd Edition of Essential Mathematics for Games and Interactive Applications 등을 참고하기 바란다.
시작하기 위해 무엇을 해야하나?
필요한것은 따로 없다! 근대 브라우져를 이용하고 있다면, WebGL이 서포트 될것이고 (구글 크롬이나 파이어폭스, 사파리), 인터넷만 있으면 된다! (지금 보고 있지만) 페이지 최하단에 있는 "Next"버튼을 눌러 다음챕터로 가면 시작이다.
아니면 , 다음과 같은 방법으로도 이 책을 접할수 있다:
- Raspberry Pi에서 브라우져 없이 예제들 돌리기
- 또는 온라인 리포 이슈들을 답하거나, 올려주세요.
---
00/README Pl (00/README-pl.md)
Wprowadzenie
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Powyższe obrazy zostały stworzone na różne sposoby. Pierwszy z nich stworzył Van Gogh, aplikując farbę warstwa po warstwie. Zajęło mu to godziny. Drugi stworzono poprzez połączenie czterech macierzy pikseli odpowiadających kolorom: cyjan, magenta, żółty i czarny. Kluczową różnicę stanowi fakt, że drugi obraz stworzony został natychmiastowo (przez komputer), a nie seryjnie, krok po kroku (przez malarza).
Ta książka jest o rewolucyjnej technice obliczeniowej, tzw. fragment shaderach (zwanych też pixel shaderami), które wznoszą cyfrowo generowane obrazy na wyższy poziom. Możesz o nich myśleć jak o ekwiwalencie maszyny drukarskiej Gutenberga dla zastosowań graficznych.
Fragment shadery dają ci pełnię kontroli nad błyskawicznym renderowaniem pikseli na ekranie. Właśnie dlatego są one używane w przeróżnych sytuacjach: od filtrów wideo w telefonach do niesamowitych trójwymiarowych gier wideo.
W następujących rozdziałach odkryjesz jak niewiarygodnie szybkie i potężne są te techniki i jak zastosować je w twojej pracy zawodowej i osobistej.
Dla kogo jest ta książka?
Ta książka jest napisana dla osób zainteresowanych creative codingiem, game developerów i inżynierów, którzy posiadają doświadczenie programistyczne, podstawową wiedzę z algebry liniowej i trygonometrii, i którzy chcą podnieść jakość swoich prac graficznych na wyższy poziom. (Jeżeli chcesz nauczyć się programować, polecam zacząć od Processing i wrócić, gdy opanujesz go do komfortowego poziomu.)
Ta książka nauczy cię jak używać shadery w celu polepszenia wydajności i wyglądu twoich projektów. Ponieważ shadery GLSL (OpenGL Shading Language) kompilują i uruchamiają się na różnorodnych platformach, będziesz w stanie zaaplikować tutaj zdobytą wiedzę do jakiegokolwiek środowiska wykorzystującego OpenGL, OpenGL ES lub WebGL. Innymi słowy, będziesz w stanie wykorzystać tę wiedzę przy tworzeniu szkiców z Processing, aplikacji z openFrameworks, interaktywnych instalacji z Cinder czy stron internetowych z Three.js i gier iOS/Android.
Jaki materiał pokrywa ta książka?
Ta książka skupia się na użyciu fragment shaderów GLSL. Najpierw zdefiniujemy czym shadery są; potem dowiemy się jak, z ich pomocą, tworzyć proceduralne kształty, wzory, tekstury i animacje. Nauczysz się podstaw języka shadingowego i jego przydatnych aplikacji w przetwarzaniu obrazów (operacje na obrazach, sploty macierzowe, rozmycia, filtry koloru, "lookup tables" i inne efekty) czy symulacji ("Gra w życie" Conwaya, model reakcji-dyfuzji Graya-Scotta, plusk wody, efekt akwareli, komórki Voronoi, itp.). Pod koniec książki zobaczymy kilka zaawansowanych technik opartych o Ray Marching.
W każdym rozdziale znajdziesz interaktywne przykłady do wypróbowania. Kiedy zmodyfikujesz kod, natychmiastowo zobaczysz zmiany. Zagadnienia mogą być abstrakcyjne i mylące, więc takie interaktywne przykłady stanowią konieczną pomoc w zrozumieniu materiału. Im szybciej złapiesz praktykę, tym prostsza będzie dalsza nauka.
Materiał, którego ta książka nie pokrywa:
To nie jest* książka o OpenGL lub WebGL. OpenGL/WebGL jest większym tematem niż GLSL czy fragment shadery. Jeśli chcesz wiedzieć więcej o OpenGL i WebGL, polecam zajrzeć do OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (zwana również "czerwoną książką") lub WebGL: Up and Running
To nie jest* książka do nauki matematyki. Choć opisane są w niej algorytmy i techniki, które opierają się na zrozumieniu algebry i trygonometrii, to nie będziemy ich szczegółowo tłumaczyć. Z pytaniami dotyczącymi matematyki polecam zajrzeć do następujących książek:
3rd Edition of Mathematics for 3D Game Programming and computer Graphics lub 2nd Edition of Essential Mathematics for Games and Interactive Applications.
Co potrzeba, żeby zacząć?
Niewiele! Jeśli masz współczesną przeglądarkę, która obsługuje WebGL (jak Chrome, Firefox czy Safari) i połączenie internetowe, to kliknij "Dalej" na dole strony, aby zacząć.
Alternatywnie, w zależności od tego, co masz albo co potrzebujesz od tej książki, możesz:
- Stworzyć wersję offline tej książki
- Uruchomić przykłady na Raspberry Pi bez przeglądarki
- Stworzyć wersję PDF tej książki do wydrukowania
- Sprawdź repozytorium GitHub tej książki, by pomóc w rozwiązywaniu problemów (issues) i podzielić się swoim kodem.
---
00/README Pt (00/README-pt.md)
Introduction
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
As imagens acima foram feitas de maneiras diferentes. A primeira foi feita pela mão do Van Gogh, aplicando-se camada a camada de tinta. Levou horas. A segunda foi produzida em segundos pela combinação de quatro matrizes de pixel: uma para ciano, uma para magenta, um para amarelo e outro pra preto. A diferença chave é que a segunda imagem é produzida de forma não-serial (ou seja, não foi passo a passo, mas todos ao mesmo tempo).
Este livro é sobre a técnica computacional revolucionária, fragment shaders, que está levando imagens geradas digitalmente a um nível mais alto. Você pode pensar nisso como sendo o equivalente à impressa de Gutenberg para gráficos.
Fragment shaders te dão controle total sobre os pixels rendereizados na tela, em velocidade super. É por isso que eles são usados para todas as finalidades, desde filtros de vídeo em celulares até videogames 3D incríveis.
Nos capítulos seguintes, você vai descobrir o quão incrivelmente rápida e poderosa é essa técnica, e como aplicá-la para seu trabalho pessoal e profissional.
Para quem é este livro?
Este livro é escrito para programadores criativos, desenvolvedores de jogos e engenheiros que têm experiência em codificação, um conhecimento básico de álgebra linear e trigonometria, e quem quiser levar seu trabalho a um novo nível excitante de qualidade gráfica (se você quer aprender como programar, eu recomendo fortemente que comece com o Processing e volte mais tarde quando estiver comfortável com isso.).
Este livro vai te ensinar como usar e integrar shaders em seus projetos, melhorando a qualidade gráfica e performance. Devido ao fato de shaders GLSL (OpenGL Shading Language) compilarem e rodarem em grande variedade de plataformas, você vai poder aplicar o que aprendeu aqui para qualquer ambiente que usar OpenGL, OpenGL ES ou WebGL. Em outras palavras, você vai poder aplicar e usar seu conhecimento com sketches do Processing, aplicações openFrameworks , instalações interativas Cinder , websites Three.js ou jogos iOS/Android.
O que este livro cobre?
Este livro vai focar no uso de shaders de pixel em GLSL. Primeiro vamos definir o que são os shaders; então vamos aprender como fazer formas proceduralmente, padrões, texturas e animações com eles. Você vai aprender as fundações da linguagem de shaders e como aplicar em cenários mais úteis, como: processamento de imagens (operações com imagens, convoluções de matrizes, blurs, filtros de cores, tabelas de lookup, e outros efeitos) e simulações (jogo da vida de Conway, reação e difusão de Gray-Scott, ondas em água, efeitos de cores de água, células de Voroni, etc). Em direção ao fim do livro, vamos ver um conjunto de técnicas avançadas baseadas em Ray Marching.
Existem exemplos interativos para você brincar em cada capítulo. Quando você muda o código, vai ver as mudanças imediatamente. os conceitos podem ser abstratos e confusos, então os exemplos interativos são essenciais para ajudá-lo a aprender o material. Quanto mais rápido você colocar os conceitos em movimento, mais fácil será o processo de aprendizagem.
O que esse livro não cobre:
Este não é* um livro de openGL ou webGL. OpenGL/webGL é um assunto maior que GLSL ou fragment shaders. Para aprender mais sobre openGL/webGL eu recomendo dar uma olhada em: OpenGL Introduction, the 8th edition of the OpenGL Programming Guide (também conhecido como o livro vermelho) ou WebGL: Up and Running
Este não é* um livro de matemática. Embora venhamos a cobrir um bom número de algoritmos e técnicas que se baseiam no entendimento de álgebra e trigonometria, não vamos explicá-los em detalhes. Para questões relacionadas com matemática eu recomendo que você tenha um desses livros por perto: 3rd Edition of Mathematics for 3D Game Programming and computer Graphics ou 2nd Edition of Essential Mathematics for Games and Interactive Applications.
O que você precisa para começar?
Não muito! Se você tem um browser moderno que possa rodar WebGL (como Chrome, Firefox ou Safari) e uma conexão à internet, clique no botão para o próximo capítulo no fim desta página para começar.
Alternativamente, baseado no que você tem, ou no que você precisa deste livro você pode:
- Fazer uma versão offline deste livro
- Rodar os exemplos em um Raspberry Pi sem um browser
- Fazer um PDF do livro para imprimir
- Checar o repositório GitHub deste livro para ajudar a resolver questões e compartilhar código.
---
00/README Tr (00/README-tr.md)
Giriş
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Yukarıdaki görüntüler farklı şekillerde oluşturuldu. İlki, Van Gogh'un eliyle katman katman boya uygulayarak yapıldı. Saatlerini aldı. İkincisi ise saniyeler içinde, piksellerden oluşan dört matrisin kombinasyonuyla üretildi: biri camgöbeği (cyan), biri macenta (magenta), biri sarı (yellow) ve biri siyah (black) için. Temel fark, ikinci görüntünün seri olmayan bir şekilde (yani adım adım değil, hepsi aynı anda) üretilmesidir.
Bu kitap, dijital olarak üretilen görüntüleri bir sonraki seviyeye taşıyan devrim niteliğindeki hesaplama tekniği olan fragment shader'lar hakkındadır. Bunu grafikler için Gutenberg'in matbaasının eşdeğeri olarak düşünebilirsiniz.
Fragment shader'lar, ekranda işlenen pikseller üzerinde süper hızlı bir şekilde tam kontrol sağlar. Bu nedenle, cep telefonlarındaki video filtrelerinden inanılmaz 3D video oyunlarına kadar her türlü durumda kullanılırlar.
İlerleyen bölümlerde bu tekniğin ne kadar inanılmaz derecede hızlı ve güçlü olduğunu ve bunu profesyonel ve kişisel çalışmalarınıza nasıl uygulayacağınızı keşfedeceksiniz.
Bu kitap kimin için?
Bu kitap, kodlama deneyimi olan, temel lineer cebir ve trigonometri bilgisine sahip ve çalışmalarını heyecan verici yeni bir grafik kalitesi seviyesine taşımak isteyen yaratıcı kodlayıcılar, oyun geliştiricileri ve mühendisler için yazılmıştır. (Eğer kodlamayı öğrenmek istiyorsanız, Processing ile başlamanızı ve kendinizi rahat hissettiğinizde daha sonra buraya geri dönmenizi şiddetle tavsiye ederim.)
Bu kitap size shader'ları projelerinize nasıl kullanacağınızı ve entegre edeceğinizi, performanslarını ve grafik kalitelerini nasıl artıracağınızı öğretecektir. GLSL (OpenGL Gölgelendirme Dili) shader'ları çeşitli platformlarda derlenip çalıştığından, burada öğrendiklerinizi OpenGL, OpenGL ES veya WebGL kullanan herhangi bir ortama uygulayabileceksiniz. Başka bir deyişle, bilginizi Processing eskizleri, openFrameworks uygulamaları, Cinder interaktif kurulumları, Three.js web siteleri veya iOS/Android oyunları ile uygulayabilir ve kullanabilirsiniz.
Bu kitap neyi kapsıyor?
Bu kitap GLSL piksel shader'larının kullanımına odaklanacaktır. Önce shader'ların ne olduğunu tanımlayacağız; sonra onlarla prosedürel şekiller, desenler, dokular ve animasyonlar yapmayı öğreneceğiz. Gölgelendirme dilinin temellerini öğrenecek ve bunu daha yararlı senaryolara uygulayacaksınız: görüntü işleme (görüntü operasyonları, matris konvolüsyonları, bulanıklaştırma, renk filtreleri, arama tabloları ve diğer efektler) ve simülasyonlar (Conway'in hayat oyunu, Gray-Scott'ın reaksiyon-difüzyonu, su dalgalanmaları, suluboya efektleri, Voronoi hücreleri, vb.). Kitabın sonuna doğru Ray Marching'e dayalı bir dizi ileri teknik göreceğiz.
Her bölümde oynamanız için interaktif örnekler bulunmaktadır. Kodu değiştirdiğinizde, değişiklikleri hemen göreceksiniz. Kavramlar soyut ve kafa karıştırıcı olabilir, bu nedenle interaktif örnekler materyali öğrenmenize yardımcı olmak için çok önemlidir. Kavramları ne kadar hızlı harekete geçirirseniz öğrenme süreci o kadar kolay olacaktır.
Bu kitabın kapsamadığı konular:
Bu bir OpenGL veya webGL kitabı değildir*. OpenGL/webGL, GLSL veya fragment shader'lardan daha büyük bir konudur. OpenGL/webGL hakkında daha fazla bilgi edinmek için şunlara göz atmanızı öneririm: OpenGL Introduction, OpenGL Programming Guide'ın 8. baskısı (kırmızı kitap olarak da bilinir) veya WebGL: Up and Running
Bu bir matematik kitabı değildir*. Cebir ve trigonometri anlayışına dayanan bir dizi algoritma ve tekniği ele alacak olsak da, bunları ayrıntılı olarak açıklamayacağız. Matematik ile ilgili sorularınız için şu kitaplardan birini yakınınızda bulundurmanızı öneririm: Mathematics for 3D Game Programming and Computer Graphics 3. Baskı veya Essential Mathematics for Games and Interactive Applications 2. Baskı.
Başlamak için neye ihtiyacınız var?
Fazla bir şeye değil! WebGL yapabilen modern bir tarayıcınız (Chrome, Firefox veya Safari gibi) ve bir internet bağlantınız varsa, başlamak için bu sayfanın sonundaki “Sonraki” Bölüm düğmesine tıklayın.
Alternatif olarak, sahip olduklarınıza veya bu kitaptan neye ihtiyacınız olduğuna bağlı olarak şunları yapabilirsiniz:
- Bu kitabın çevrimdışı bir sürümünü yapın
- Örnekleri tarayıcı olmadan bir Raspberry Pi üzerinde çalıştırın
- Kitabın yazdırılabilir bir PDF'ini oluşturun
- Sorunları çözmeye yardımcı olmak ve kod paylaşmak için bu kitabın GitHub deposunu kontrol edin.
---
00/README Vi (00/README-vi.md)
Giới thiệu
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Hai bức ảnh trên đây được tạo ra bằng những cách khác nhau. Bức đầu tiên được vẽ bởi Van Gogh bằng nhiều lớp sơn chồng lên nhau. Nó đã ngốn của ông ấy hàng giờ đồng hồ. Bức thứ hai được tạo ra trong chớp mắt từ 4 ma trận điểm ảnh: một cho màu xanh (Cyan), một cho màu hồng (Magenta), một cho màu vàng (Yellow) và một cho màu đen (Black). Điểm khác biệt chính là: các lớp của bức tranh thứ hai được tạo ra đồng thời chứ không phải tuần tự.
Quyển sách này nói về một kỹ thuật mang tính cách mạng trong Đồ họa Máy tính, fragment shader, giúp nâng tầm đồ hoạ kỹ thuật số lên một tầm cao mới. Bạn có thể nghĩ sức ảnh hưởng của nó tương đương với cỗ máy in của Gutenberg khi xưa vậy.
Fragment shader cho bạn toàn quyền kiểm soát các điểm ảnh được render trên màn hình cực kỳ nhanh chóng. Đó là lí do tại sao nó được dùng trong mọi lĩnh vực của Đồ hoạ máy tính, từ các bộ lọc video trên điện thoại di động tới các trò chơi điện tử 3D đáng kinh ngạc.
Ở các chương tiếp theo, bạn sẽ khám phá ra kỹ thuật này nhanh và mạnh kinh khủng tới mức nào, và làm thế nào để áp dụng nó vào công việc.
Quyển sách này dành cho những ai ?
Quyển sách này được viết cho các kỹ sư và lập trình viên sáng tạo, những người đã có kinh nghiệm lập trình và kiến thức cơ bản về Giải tích và Lượng giác, và cho những ai muốn nâng tầm chất lượng đồ hoạ trong tác phẩm của mình. (Nếu bạn muốn học lập trình, tôi khuyên bạn nên bắt đầu từ trang web Processing rồi quay lại đây khi đã cảm thấy đủ kiến thức.
Quyển sách này sẽ chỉ cho bạn cách dùng các shader và cách áp dụng shader vào các dự án để cải thiện hiệu năng cũng như chất lượng đồ hoạ. Vì các shader viết bằng GLSL (viết tắt của OpenGL Shading Language) sẽ được biên dịch và chạy trên rất nhiều nền tảng khác nhau, bạn có thể ứng dụng những gì học được ở đây cho bất kỳ môi trường nào sử dụng OpenGL, OpenGL ES hoặc WebGL. Cụ thể, bạn có thể ứng dụng những kiến thức này cho các bản vẽ Processing, các ứng dụng openFrameworks, các cỗ máy Cinder tương tác được, các website sử dụng Three.js hay các trò chơi trên iOS/Android.
Nội dung của quyển sách này là gì ?
Quyển sách này sẽ tập trung vào việc sử dụng các pixel shader viết bằng GLSL. Đầu tiên ta sẽ định nghĩa thế nào là shader, rồi mới học cách tạo các hình, mẫu, ảnh bằng Toán học và làm chúng chuyển động bằng shader. Bạn sẽ được học các kiến thức nền tảng của shader và áp dụng chúng vào các chuyên ngành khác như: Xử lý ảnh
(Các phép toán xử lý ảnh, ma trận chập, làm mờ, bộ lọc, bảng quy chiếu và các hiệu ứng khác), mô phỏng (Mô phỏng tiến trình sống theo phương pháp Conway, mô hình Phản ứng - Khuếch tán của Gray-Scott, các gợn sóng trên mặt nước, hiệu ứng màu nước, nhiễu Voronoi, vân vân ...). Tới cuối quyển sách, ta sẽ nói về các kỹ thuật cao cấp hơn dựa trên thuật toán Dò tia - Ray Marching.
Tất cả các chương đều có các ví dụ tương tác được để bạn khám phá. Khi bạn sửa code, bạn sẽ thấy thay đổi ngay lập tức. Các khái niệm có thể hơi trừu tượng và dễ gây nhầm lẫn, nên các ví dụ tương tác được sẽ rất cần thiết để hỗ trợ bạn học. Bạn nắm vững các khái niệm càng nhanh thì bạn học càng dễ.
Những nội dung nằm ngoài phạm vi của quyển sách:
Đây không phải* sách nói về OpenGL hay WebGL. OpenGL/WebGL là chủ đề rộng lớn hơn cả GLSL và fragment shader. Để tìm hiểu thêm về OpenGL/WebGL, tôi khuyên bạn nên đọc Giới thiệu OpenGL, Hướng dẫn lập trình OpenGL - xuất bản lần 8 (còn được biết tới với tên gọi là The Red Book) hoặc WebGL: Up and Running
Đây không phải* sách Toán. Mặc dù ta có đề cập tới một vài thuật toán và kỹ thuật liên quan tới Giải tích và Lượng giác, nhưng chúng tôi sẽ không giải thích chúng một cách chi tiết. Để tiện giải đáp các thắc mắc về Toán, tôi khuyên bạn nên có một trong số các quyển sách sau: Ứng dụng Toán trong lập trình và đồ hoạ máy tính - xuất bản lần 3 hoặc Kiến thức Toán cần thiết cho lập trình Game và Ứng dụng real-time.
Bạn cần gì để bắt đầu ?
Chỉ vài thứ thôi! Nếu bạn có trình duyệt có thể xử lý WebGL (như Chrome, Firefox hoặc Safari) và kết nối Internet, click nút _"Next >>>"_ ở cuối trang này để bắt đầu ngay.
Hoặc, tuỳ theo bạn có gì và cần gì từ quyển sách, bạn có thể:
- Tạo quyển sách phiên bản không cần Internet
- Chạy các ví dụ trên Raspberry Pi mà không cần trình duyệt
- Tạo phiên bản PDF của sách để in
- Xem repository trên GitHub của quyền sách này để giúp sửa và chia sẻ code.
---
01/README (01/README.md)
Getting started
What is a fragment shader?
In the previous chapter we described shaders as the equivalent of the Gutenberg press for graphics. Why? And more importantly: what's a shader?
If you already have experience making drawings with computers, you know that in that process you draw a circle, then a rectangle, a line, some triangles until you compose the image you want. That process is very similar to writing a letter or a book by hand - it is a set of instructions that do one task after another.
Shaders are also a set of instructions, but the instructions are executed all at once for every single pixel on the screen. That means the code you write has to behave differently depending on the position of the pixel on the screen. Like a type press, your program will work as a function that receives a position and returns a color, and when it's compiled it will run extraordinarily fast.
Why are shaders fast?
To answer this, I present the wonders of parallel processing.
Imagine the CPU of your computer as a big industrial pipe, and every task as something that passes through it - like a factory line. Some tasks are bigger than others, which means they require more time and energy to deal with. We say they require more processing power. Because of the architecture of computers the jobs are forced to run in a series; each job has to be finished one at a time. Modern computers usually have groups of four processors that work like these pipes, completing tasks one after another to keep things running smoothly. Each pipe is also known as a thread.
Video games and other graphic applications require a lot more processing power than other programs. Because of their graphic content they have to do huge numbers of pixel-by-pixel operations. Every single pixel on the screen needs to be computed, and in 3D games geometries and perspectives need to be calculated as well.
Let's go back to our metaphor of the pipes and tasks. Each pixel on the screen represents a simple small task. Individually each pixel task isn't an issue for the CPU, but (and here is the problem) the tiny task has to be done to each pixel on the screen! That means in an old 800x600 screen, 480,000 pixels have to processed per frame which means 14,400,000 calculations per second! Yes! That’s a problem big enough to overload a microprocessor. In a modern 2880x1800 retina display running at 60 frames per second that calculation adds up to 311,040,000 calculations per second. How do graphics engineers solve this problem?
This is when parallel processing becomes a good solution. Instead of having a couple of big and powerful microprocessors, or pipes, it is smarter to have lots of tiny microprocessors running in parallel at the same time. That’s what a Graphic Processor Unit (GPU) is.
Picture the tiny microprocessors as a table of pipes, and the data of each pixel as a ping pong ball. 14,400,000 ping pong balls a second can obstruct almost any pipe. But a table of 800x600 tiny pipes receiving 30 waves of 480,000 pixels a second can be handled smoothly. This works the same at higher resolutions - the more parallel hardware you have, the bigger the stream it can manage.
Another “super power” of the GPU is special math functions accelerated via hardware, so complicated math operations are resolved directly by the microchips instead of by software. That means extra fast trigonometrical and matrix operations - as fast as electricity can go.
What is GLSL?
GLSL stands for OpenGL Shading Language, which is the specific standard of shader programs you'll see in the following chapters. There are other types of shaders depending on hardware and Operating Systems. Here we will work with the OpenGL specs regulated by Khronos Group. Understanding the history of OpenGL can be helpful for understanding most of its weird conventions, for that I recommend taking a look at: openglbook.com/chapter-0-preface-what-is-opengl.html
Why are Shaders famously painful?
As Uncle Ben said “with great power comes great responsibility,” and parallel computation follows this rule; the powerful architectural design of the GPU comes with its own constraints and restrictions.
In order to run in parallel every pipe, or thread, has to be independent from every other thread. We say the threads are blind to what the rest of the threads are doing. This restriction implies that all data must flow in the same direction. So it’s impossible to check the result of another thread, modify the input data, or pass the outcome of a thread into another thread. Allowing thread-to-thread communications puts the integrity of the data at risk.
Also the GPU keeps the parallel micro-processor (the pipes) constantly busy; as soon as they get free they receive new information to process. It's impossible for a thread to know what it was doing in the previous moment. It could be drawing a button from the UI of the operating system, then rendering a portion of sky in a game, then displaying the text of an email. Each thread is not just blind but also memoryless. Besides the abstraction required to code a general function that changes the result pixel by pixel depending on its position, the blind and memoryless constraints make shaders not very popular among beginning programmers.
Don't worry! In the following chapters, we will learn step-by-step how to go from simple to advanced shading computations. If you are reading this with a modern browser, you will appreciate playing with the interactive examples. So let's not delay the fun any longer and press Next >> to jump into the code!
---
01/Notes (01/notes.md)
Scott M
* You say “pipes” and also “threads”. This is a small thing but I wonder if you can reconcile those metaphors. I think in terms of threads, but “pipes” makes more sense with your illustration. Also works better with the concept of a “pipeline” or “graphics/rendering pipeline”.
Nicolas B
* clearly answer the "what is a fragment shader?" question in some sort of recap paragraph.
* beef up explanations around the compilation step (a brief overview will do).
---
01/README De (01/README-de.md)
Einstieg
Was ist ein Fragment-Shader?
Im vorangegangenen Kapitel haben wir Fragment-Shader als eine Art Gutenbergsche Druckerpresse für Computergrafiken beschrieben. Nun, wie kommen wir darauf? Und was genau soll ein Shader sein?
Falls Du schon Erfahrung mit der Erstellung von Computergrafiken gesammelt hast, kennst Du bestimmt die folgende Vorgehensweise: Man malt per Programmbefehl Kreise, Rechtecke, Dreiecke und Linien, damit auf dem Bildschirm nach und nach die gewünschte Grafik entsteht. Dieser Vorgang ähnelt von Hand einen Brief oder Buch zu verfassen. Es handelt sich um eine Folge von Anweisungen (den Zeichenoperationen), die Schritt für Schritt abgearbeiet werden müssen.
Auch Shader sind nur eine Abfolge von Anweisungen, doch werden diese Anweisungen alle gleichzeitig für jeden Bildpunkt (Pixel) in der Grafik ausgeführt. Das bedeutet also, dass das von Dir geschriebene Shader-Programm die Grafik abhängig von der sich ändernden Position eines einzelnen Bildpunkts beschreiben muss. Der Shader arbeitet dabei als eine Funktion, die die Koordinaten des jeweiligen Bildpunktes erhält und als Ergebnis die Farbe für diesen Bildpunkt zurückliefert. Ist der Shader einmal kompiliert, läuft dieser Prozess unglaublich schnell und für sehr viele Bildpunkte gleichzeitig ab.
Warum arbeiten Shader schnell?
Diese Frage beantwortet sich, wenn wir uns das Prinzip der parallelen Verarbeitung anschauen.
Stelle Dir den Prozessor in Deinem Computer als eine große Pipeline vor und jede zu bearbeitende Aufgabe als etwas, das durch die Pipeline fließt - wie an einem Fließband innerhalb einer Fabrik. Einige Aufgaben sind größer als andere, weshalb ihre Abarbeitung mehr Zeit und mehr Energie in Anspruch nimmt. Wir sagen dann, diese Aufgaben benötigen mehr „Prozessorleistung“. Der Aufbau des Computers zwingt die verschiedenen Aufgaben dazu, eine nach der anderen durch die Pipeline zu strömen. Die nächste Aufgabe ist an der Reihe, sobald die vorangehende Aufgabe erfolgreich abgearbeitet wurde. Moderne Prozessoren verfügen in der Regel über mehrere Kerne (typischerweise zwei, vier oder acht), von denen jeder wie eine Pipeline arbeitet, so dass mehrere Ströme von Aufgaben (engl. „Threads“) gleichzeitig abgearbeitet werden können.
Videospiele und leistungsstarke Grafikanwendungen benötigen in der Regel wesentlich mehr Prozessorleistung als andere Computerprogramme. Weil sie grafikorientiert arbeiten, müssen diese Programme eine enorme Anzahl an Pixel-Operationen ausführen. Jeder einzelne Bildpunkt muss individuell berechnet werden, und bei 3D-Anwendungen kommen noch perspektivische Verzerrungen sowie das Spiel von Licht und Schatten hinzu.
Kommen wir noch einmal auf das Bild einer Pipeline zurück. Jeder einzelne Bildpunkt verkörpert eine Rechenaufgabe, die durch die Pipeline abgearbeitet werden muss. Einzeln genommen macht das nicht viel Arbeit - der enorme Aufwand entsteht erst durch die schiere Masse an Pixeln auf dem Bildschirm. Schon bei einer Auflösung von 800 x 600 Bildpunkten müssen pro Bild 480.000 Pixels berechnet werden. Für eine flüssige Darstellung mit 30 Bildern pro Sekunde ergeben sich also bereits 14.400.000 Berechnungen pro Sekunde. Das ist eine Dimension, bei der auch moderne Prozessoren an ihre Grenzen stoßen. Und bei einem hochauflösenden Retina-Display mit 2880 x 1800 Bildpunkten und einer Wiederholrate von 60 Bildern pro Sekunde kommen sogar 311.040.000 Berechnungen pro Sekunde zusammen. Fragt sich also, wie ein Computersystem dies bewältigen kann?
Hier kommt die gleichzeitige - parallele - Berechnung möglichst vieler Bildpunkte ins Spiel. An die Stelle weniger großer und immens leistungsfähiger Pipelines (sprich: Mikroprozessoren) treten viele kleine, einfachere Prozessoren, die parallel arbeiten. Sie bilden den Kern einer sogenannten „Graphic Processing Unit“, kurz GPU.
Stelle Dir eine solche GPU wie ein Feld aus lauter Pipelines vor - eine neben der anderen. Jeder Bildpunkt entspricht einem Tischtennisball, der durch eine solche Pipeline hindurchmuss. 14.400.000 Bälle pro Sekunde sind wahrscheinlich zu viel für jede einzelne noch so dicke Pipeline. Hat man aber ein Feld aus 800 x 600 kleinen Pipelines, muss jede nur 30 Bälle pro Sekunde verarbeiten, damit man eine flüssige Darstellung auf dem Bildschirm erhält. Und das ist durchaus machbar. Gleiches gilt für höhere Auflösungen und größere Bildwiederholraten: Je mehr Pipelines parallel arbeiten, desto mehr Bildpunkte können sie pro Zeiteinheit bewältigen.
Die enorme Leistung einer solchen GPU rührt aber auch aus einer anderen Quelle: Viele für die Grafikberechnung unverzichtbare mathematische Operationen lassen sich direkt in der Hardware, in jeder einzelnen Pipeline, parallel ausführen. Es wird also keine zusätzliche Software benötigt, um diese Berechnungen in mehreren Schritten aus einfachen Grundoperationen zusammenzusetzen. Das gilt beispielsweise für die wichtigen trigonometrischen Funktionen und für Matrizenoperationen. Das bringt die Darstellung zusätzlich auf Trab.
Was ist GLSL?
GLSL ist die Abkürzung für „OpenGL Shading Language“, einem weit verbreiteten Standard für Shader-Programme, mit dem sich die folgenden Kapitel beschäftigen. Es gibt auch andere Arten von Shadern, je nach Grafikhardware und Betriebssystem. Wir orientieren uns hier an der wichtigen OpenGL-Spezifikation, die von der Khronos Gruppe erarbeitet wurde. Wenn man die Geschichte von OpenGL kennt, kann man einige der teilweise etwas merkwürdigen Konstrukte und Konventionen besser verstehen. Allen Interessierten empfehle ich deshalb einen Blick auf openglbook.com/chapter-0-preface-what-is-opengl.html.
Warum sind Shader so anspruchsvoll?
Mein reicher Onkel Willi aus Amerika sagte früher immer zu mir: „Denk dran, Junge, unglaublich viel Power bedeutet immer auch unglaublich viel Verantwortung“. Dies gilt in gewisser Weise auch für das Feld des „Parallel Computings“. Die enorme Leistungsfähigkeit moderner GPUs erzeugt ihre eigenen Regeln und Abhängigkeiten. Und die wollen beachtet werden, damit wir nicht über das Ziel hinausschießen.
Damit jede Pipeline innerhalb der GPU parallel an ihrer Aufgabe arbeiten kann, muss sie unabhängig von den anderen Pipelines sein. Sie ist quasi blind dafür, was die anderen gerade tun. Dies bringt u.a. die Einschränkung mit sich, dass alle Daten nur in eine Richtung fließen können. Es ist praktisch unmöglich, auf die Ergebnisse einer anderen Pipeline einzugehen, deren Ergebnis abzurufen und damit eine dritte Pipeline zu füttern.
Außerdem hält die GPU ihre zahlreichen Mikroprozessoren (Pipelines) ständig beschäftigt. Sobald eine Pipeline mit der Berechnung der Farbe für einen Bildpunkt fertig ist, wird sie auch schon mit dem nächsten Bildpunkt „gefüttert“. Aus Sicht des Shader-Programms gibt es quasi kein Vorher und Nachher, sondern immer nur den aktuell zu berechnenden Bildpunkt. Das Shader-Programm in jeder Pipeline ist also nicht nur blind für alle anderen, sondern auch ohne Erinnerung für das Gewesene. Das ist ein Teil der Herausforderung, die die Entwicklung von Shadern bei Programmieranfängern nicht unbedingt beliebt macht.
Aber keine Angst! In den folgenden Kapiteln werden wir uns Schritt für Schritt von einfachen Shadern hin zu komplexen Shadern voran arbeiten. Sofern Du diesen Text mit einem modernen Browser liest, wirst Du ganz einfach mit den zahlreichen interaktiven Beispielprogrammen experimentieren können. Also, lass uns gleich anfangen, indem Du auf die „Next“-Schaltfläche unten auf der Seite klickst.
---
01/README Es (01/README-es.md)
Comenzando
Qué es un fragment shader?
En el capítulo anterior comparamos a los shaders con la invención de la imprenta de Gutenberg. ¿Por qué? Y más importante: ¿Qué es un shader?
Si ya tienes experiencia dibujando con computadoras, sabrás que en ese proceso dibujas un círculo, luego un rectángulo, una línea, algunos triángulos, hasta que por fin compones la imagen que querías. Ese proceso es muy similar a escribir una carta o un libro a mano, es un conjunto de instrucciones, una tarea después de la otra.
Los shaders son también un conjunto de instrucciones, pero estas son ejecutadas todas al mismo tiempo por cada pixel de la pantalla. Eso significa que el código que escribes tiene que comportarse de manera diferente dependiendo de su posición en la pantalla. Como una prensa tipográfica, tu programa trabajará como una función que recibe posición y devuelve color, y que al ser compilada se ejecutará a una velocidad extraordinaria.
Por qué son rápidos los shaders?
Para responder esto hay que hablar de las maravillas del parallel processing.
Imagina que tu CPU es un gran tubo industrial y que cada tarea pasa por ahí como si fuese una linea de producción. Algunas tareas son más grandes que otras, esto quiere decir que algunas consumen más tiempo y energía que el resto. Solemos decir que estas tareas requieren más tiempo de proceso. Debido a la arquitectura de las computadoras estas tareas son forzadas a correr en serie; cada trabajo debe ser terminado, uno después del otro. Las computadoras modernas usualmente cuentan con un grupo de procesadores que trabajan como estos tubos, completando tareas, una después de la otra. Cada uno de estos tubos es también conocido como thread.
Los videojuegos y otras aplicaciones gráficas requieren mucho más tiempo de proceso que otros programas. Debido a su contenido gráfico es necesario hacer muchas operaciones numéricas por pixel. Cada pixel de la pantalla necesita ser computado, y en el caso de los videojuegos en 3D también hay que calcular las geometrías y las perspectivas.
Volvamos a pensar en la metáfora de los tubos y las tareas. Cada pixel de la pantalla representa una pequeña tarea a realizar. Individualmente cada tarea no es un gran problema para el CPU, pero (y aquí está el problema) ¡Esta pequeña tarea deberá ser ejecutada por cada pixel! Eso significa que en una antigua pantalla de 800x600 pixeles ¡Tendremos que procesar 480000 pixeles por frame, es decir 14400000 cálculos por segundo! ¡Sí! Ese es un problema lo suficientemente grande como para sobrecargar al microprocesador. En una pantalla retina display moderna de 2880x1800 pixeles, corriendo a 60 frames por segundo, los cálculos aumentarían a 311040000 por segundo. ¿Cómo hicieron los ingenieros gráficos para solucionar este problema?
Aquí es donde procesar en paralelo se vuelve una buena solución. En vez de tener un par de procesadores grandes y poderosos, o tubos, es mucho más inteligente tener muchos pequeños procesadores funcionando en paralelo al mismo tiempo. Eso es la GPU (Graphic Processor Unit).
Imagina que los pequeños procesadores conforman una mesa de tuberías, y que la información de cada pixel es una pelota de ping pong. 14400000 pelotas de ping pong en un segundo pueden obstruir a cualquier tubería, pero si en cambio es una mesa de 800x600 tuberías y recibe 30 olas de 480000 pixeles por segundo, se puede manejar de una manera fluída sin problemas. Esto funciona de la misma manera con resoluciones más grandes, cuanto más hardware en paralelo tengas, es mayor el flujo de pixeles que se puede manejar.
Otro "superpoder" de la GPU es que algunas funciones matemáticas especiales son aceleradas vía hardware, la matemática más compleja es solucionada directamente en el microchip en vez resolverlo en el software. Eso signfica que tendremos una velocidad extra en cálculos trigonométricos u operaciones de matrices que irán tan rápido como la electricidad.
¿Qué es GLSL?
GLSL es la sigla de openGL Shading Language, que es el standard específico de shaders que veremos en los próximos capítulos. Hay otros tipos de shaders que varían dependiendo del hardware y del sistema operativo. Aquí trabajaremos con las especificaciones reguladas por el Khronos Group. Entender la historia de OpenGL puede ser útil para comprender la mayoría de las convenciones raras, te recomiendo echarle un vistazo a: openglbook.com/chapter-0-preface-what-is-opengl.html
¿Por qué los shaders tienen mala reputación?
Como dijo el tio Ben "un gran poder conlleva una gran responsabilidad", y la computación paralela sigue esta regla; el poderoso diseño de arquitectura de la GPU viene con sus propias limitaciones y restricciones.
Para que cada tubo, o thread, pueda correr en paralelo es necesario que cada uno sea independiente del otro. Es decir que los threads son ciegos y no saben lo que los demás threads están haciendo. Esta restricción implica que toda la información debe fluir en la misma dirección, por lo tanto es imposible conocer el resultado de otro thread. Permitir la comunicación entre threads pondría en riesgo la integridad de los datos.
Además la GPU deja constantemente ocupados a los micro-procesadores (los tubos); tan pronto como terminan una tarea reciben nueva información para procesar. Es imposible para cada thread saber lo que estaba haciendo en el momento previo. Se podría dibujar un botón de una UI de un sistema operativo, luego renderizar una porción del cielo de un videojuego, y a continuación mostrar el texto de un mail. Cada thread no solamente es ciego sino que tampoco tiene memoria. Más allá de la abstracción necesaria para poder crear una función que cambie de resultado pixel a pixel, dependiendo de su posición, la incapacidad de ver a los demás threads y la falta de memoria, hacen que los shaders no sean muy populares entre los programadores principiantes.
¡No te preocupues! En los próximos capítulos aprenderemos paso a paso, desde los shaders más sencillos a los casos más avanzados. Si estás leyendo esto desde un navegador moderno, vas a poder jugar con los ejemplos interactivos. No esperes más y presiona Siguiente >> para ir al próximo capítulo.
---
01/README Fa (01/README-fa.md)
شروع
فرگمنت شیدر چیست؟
در قسمت قبل شیدر ها را معادل دستگاه گوتنبرگ برای گرافیک توصیف کردیم. چرا؟ و مهم تر اینکه: شیدر چیست؟
اگر تجربه نقاشی در کامپیوتر را داشته باشید میدانید که در این فرایند، دایره، مستطیل و خط هارا یکی پس از دیگری میکشید تا شکل مورد نظر خود را نهایی کنید، این فرایند شبیه نوشتن کتاب است، در واقع این کار ها مجموعه ای از دستور العمل ها هستند که وظایف را یکی پس از دیگری انجام میدهند.
شیدر ها هم مجموعه ای از دستورالعمل ها هستند، اما دستورالعمل هایی که به یکباره برای هر پیکسل روی صفحه اجرا میشود. این بدان معناست، کدی که مینویسید بسته به موقعیت پیکسل روی صفحه نمایش باید رفتار متفاوتی انجام دهد. برنامه شما مانند تابعی کار میکند که موقعیت را دریافت میکند و یک رنگ را بر میگرداند، زمانی که کد شما کامپایل میشود، این عملیات بسیار سریع انجام میشود.
چرا شیدر ها سریع هستند؟
برای پاسخ به این سوال من شگفتی پردازش موازی را به شما معرفی میکنم.
پردازه مرکزی(CPU) رایانه خود را به عنوان یک لوله بزرگ صنعتی در نظر بگیرید، و هر تسکی را مانند چیزی که از داخل آن عبور میکند بپندارید. بعضی تسک ها بزرگ تر از بقیه هستند پس زمان و انرژی بیشتری برای پردازش آن ها نیاز است. به نوعی میگوییم این نوع تسک ها به قدرت پردازش بالاتری نیاز دارند. به دلیل معماری رایانه ها تسک ها مجبور هستند به صورت سری اجرا شوند، تسک ها باید یک به یک تمام شوند. رایانه های مدرن معمولا دارای گروه های چهارتایی از پردازنده ها هستند، که مانند این لوله ها کار میکنند و وظایف را یکی پس از دیگری انجام میدهد تا کار ها بدون مشکل انجام شود، هر لوله را به عنوان یک ترد(thread) هم میشناسند.
بازی های ویدئویی و سایر برنامه های گرافیکی به پردازش بیشتری نسبت به برنامه های معمول نیاز دارند. به دلیل محتویات زیاد، آن ها باید باید مقادیر زیادی عملیات پیکسل به پیکسل انجام دهند. هر یک از پیکسل ها باید محاسبه شوند، همچنین در بازی های سه بعدی باید عملیات های بیشتری مثل محاسبات هندسی و پرسپکتیو هم انجام شود.
بیایید به استعاره خود در مورد لوله ها و تسک ها برگردیم، هر پیکسل رو صفحه نمایش یک تسک کوچک و ساده را نشان میدهد، به طور جداگانه تسک هر پیکسل برای CPU مسئله ای نیست. اما اینکه این تسک های کوچک باید برای هر پیکسل انجام شود برای CPU مشکل ساز است. این یعنی در صفحات قدیمی 800x600 باید 480,000 پیکسل در هر فریم پردازش شود که به معنی 14,400,000 محاسبه در ثانیه است! بله این مقدار به قدری بزرگ هست که یک ریزپردازنه را سرریز کند. در یک نمایشگر مدرن 2880x1800 با نرخ 60 فریم بر ثانیه، این محاسبات به 311,040,000 محاسبه در ثانیه میرسد. چگونه مهندسان گرافیک این مشکل را حل میکنند؟
این زمانی است که پردازش موازی به یک راه حل خوب تبدیل میشود، منطقی است که به جای داشتن چند ریز پردازنده بزرگ یا قدرتمند(لوله)، ریزپردازنده های کوچکتری و بیشتری به صورت موازی کار کنند.واحد پردازنده گرافیکی(GPU) همین طور عمل میکند.
ریز پردازنده های کوچک را مانند جدولی از لوله ها وداده های هر پیکسل را مانند یک توپ تصور کنید، 14,400,000 توپ میتواند هر لوله ای را مسدود کند، اما میتوان یک جدول از 800x600 لوله کوچک را که 30 موج 480,000 تایی پیکسل در ثانیه دریافت میکند را به راحتی کنترل کرد. در وضوح بالاتر هم به همین صورت عمل میکند. هرچه سخت افزار های موازی بیشتری داشته باشید، به همان نسبت جریان بیشتری را میتوانید مدیریت کنید.
از دیگر قدرت های فوق العاده GPU عملکرد های ویژه ریاضی آن است که از طریق سخت افزار سریع تر میشود، یعنی این عملیات ریاضی به جای استفاده از نرم افزار مستقیما توسط میکرو تراشه حل میشوند، به عبارتی عملکرد های مثلثاتی و ماتریسی به سرعت الکتریسیته میتوانند سریع باشند.
GLSL چیست؟
مخفف OpenGL Shading Language است. که استانداردی خاص از برنامه های شیدر هست و در فصل بعدی خواهید دید. بسته به سخت افزار و سیستم عامل ها انواع دیگر شیدر نیز وجود دارند. در اینجا ما با OpenGL تنظیم شده توسط گروه Khronos کار خواهیم کرد. دانستن تاریخچه OpenGL میتواند به درک بیشتر قرار داد های عجیب و غریب آن مفید باشد، برای همین توصیه میکنیم نگاهی به این موضوع داشته باشید: openglbook.com/chapter-0-preface-what-is-opengl.html
چرا شیدر ها به دردناک بودن معروفند؟
همانطور که عمو بن گفت:"قدرت زیاد، با مسئولیت بزرگی همراه است"، و محاسبه موازی ازین قاعده پیروی میکند، طراحی معماری قوی GPU محدودیت ها و خصوصیات مخصوص به خودش را دارد.
برای این که به صورت موازی هر لوله یا ترد اجرا شود. باید از هر ترد دیگری مستقل باشد، ترد ها نسبت به آنچه بقیه ترد ها انجام میدهد نا آگاه هستند. این محدودیت نشان میدهد که همه داده ها باید در یک جهت جریان داشته باشند. پس بررسی نتیجه یک ترد دیگر، اصلاح داده های ورودی و یا انتقال نتیجه یک ترد به ترد دیگر غیر ممکن است. اگر به ترد ها اجازه ارتباط داده شود، یکپارچگی داده ها در معرض خطر قرار خواهد گرفت.
همچنین GPU پردازنده های موازی را به طور مداوم مشغول نگه میدارد، به محض اینکه آزاد میشوند، داده های جدید برای پردازش دریافت میکنند. برای یک ترد غیر ممکن است که بداند که لحظه قبلی چه کاری انجام داده. عملیات قبلی آن میتواند کشیدن یک دکمه برای UI سیستم عامل بوده باشد یا رندر یک آسمان در بازی ویدئویی. هر ترد نه تنها کور(Blind) است(نا آگاه از عملیات ترد های دیگر) بلکه بی حافظه نیز هست. علاوه بر دانستن مفهوم نحوه کد نویسی بر اساس موقعیت هر پیکسل، این کور بودن و بی حافظه بودن، باعث میشود، شیدر ها در میان برنامه نویسان تازه محبوبیت نداشته باشد.
نگران نباش در فصل های بعد، گام به گام یاد خواهیم گرفت چگونه محاسبات ساده تا پیشرفته شیدر را انجام دهیم. اگر از مرورگر های مدرن استفاده میکنی، میتوانی از مثال های تعاملی هم استفاده کنی، برای شروع روی کلید بعدی بزن.
---
01/README Fr (01/README-fr.md)
Bien démarrer
Qu'est-ce qu'un fragment shader ?
Au chapitre précédent, nous avons décrit les shaders comme l'équivalent de l'avènement de la presse par Gutenberg pour l'impression. Pourquoi cette comparaison mais surtout : Qu'est-ce qu'un shader ?
Si vous avez déjà eu une expérience dans la réalisation de dessins avec un ordinateur, vous savez qu'il faut dessiner un cercle, puis un rectangle, une ligne, quelques triangles jusqu'à pouvoir composer l'image que l'on souhaite.
Ce processus est très similaire à l'écriture d'une lettre ou d'un livre voire à la peinture, c'est un ensemble d'instructions, exécutées en série, l'une après l'autre.
Les shaders sont également des ensembles d'instructions, mais à la différence des API de dessin classiques, toutes les instructions sont exécutées sur chaque pixel de l'écran.
Cela signifie que le code va devoir se comporter différemment selon la position du pixel sur l'écran.
Comme la presse d'imprimerie, votre programme est une fonction à laquelle on passe une position et qui nous renvoie une couleur et une fois compilé, ce programme peut s'exécuter extrêmement rapidement.
Pourquoi les shaders sont rapides ?
Pour répondre à cela, je présente les merveilles du traitement en parallèle.
Imaginez le CPU de votre ordinateur comme un tuyau et chacune des opérations que vous lui demandez d'exécuter comme quelque chose qui passe par ce tuyau - comme dans une usine. Certaines opérations seront sans doute plus importantes que d'autres et demanderont plus de temps et d'énergie pour être traitées. On dit qu'elles nécessitent plus de ressources ou de puissance de calcul. Du fait de l'architecture des ordinateurs, les opérations sont exécutées en série; chaque opération doit être terminée avant que le CPU puisse traiter la suivante. Les ordinateurs récents ont généralement plusieurs processeurs qui jouent le rôle de tuyaux, ce qui permet d'exécuter les opérations une après l'autre tout en gardant une certaine fluidité. Ces tuyaux sont ce que l'on appelle des threads.
Les jeux vidéos et autres applications graphiques demandent beaucoup plus de puissance de calcul que les autres programmes.
Par nature, ils demandent de grandes quantités d'opérations au pixel, chaque changement d'image demande de recalculer l'ensemble des pixels de l'écran. Dans les applications 3D, on doit également mettre à jour les modèles, les textures, les transformations etc. ce qui rajoute encore plus de charge au CPU.
Revenons à notre métaphore des tuyaux et des opérations. Chaque pixel à l'écran représente une simple petite opération. En soi, le traitement d'une opération n'est pas un problème pour le CPU, mais (et c'est ici que se trouve le problème) il faut appliquer cette petite opération sur chaque pixel à l'écran ! Par exemple, sur un vieux moniteur ayant une résolution de 800x600, 480 000 pixels ont besoin d'être traités par frame ce qui équivaut à 14 600 000 calculs par seconde ! C’est une opération assez importante pour surcharger un microprocesseur. Sur un écran rétina moderne ayant une résolution de 2880x1800 cadencé à 60 frames par seconde, cela représente un total de 311 040 000 calculs par seconde. Comment les ingénieurs graphiques résolvent-ils ce problème?
C'est là qu'intervient le traitement parallèle (parallel processing). Au lieu d'avoir quelques gros et puissants microprocesseurs, ou tuyaux, on préfère avoir de nombreux petits microprocesseurs qui tournent en parallèle et simultanément. C'est l'essence même du GPU (Graphics Processing Unit).
Imaginez ces petits microprocesseurs comme une trame de tuyaux, et les données de chaque pixel comme des balles de ping-pong. 14 400 000 balles de ping-pong par seconde pourraient obstruer presque n'importe quel tuyau. Mais une trame de 800x600 petits tuyaux recevant 30 vagues de 480 000 balles de ping-pong par seconde peuvent traiter la charge facilement. Et ça marche également pour des résolutions plus élevées; plus le matériel est capable de traiter d'informations en parallèle, plus il pourra traiter des flux importants.
Un autre pouvoir magique du GPU c'est l'accélération matérielle de certaines fonctions mathématiques. Certaines fonctions souvent complexes seront traitées directement par le matériel au lieu de passer par la couche logicielle. Ce qui signifie que certaines opérations mathématiques un peu complexes comme les transformations de matrices et les opérations trigonométriques seront traitées extrêmement rapidement - à la vitesse de l'électricité.
Qu'est ce que le GLSL ?
GLSL est l'acronyme de "OpenGL Shading Language" (où GL signifie Graphics Library), c'est une norme servant à écrire les programmes de shaders que nous aborderons dans les chapitres suivants. Il existe d'autres types de shaders, selon les plateformes et le matériel utilisé, nous nous concentrerons sur OpenGL, dont les spécifications sont faites par Khronos Group. Comprendre l'histoire d'OpenGL peut être utile pour comprendre certaines conventions un peu bizarres, si cela vous intéresse, vous pouvez vous reporter à openglbook.com/chapter-0-preface-what-is-opengl.html.
Pourquoi les gens tremblent en entendant le mot Shader ?
Comme dit Uncle Ben dans Spiderman: "with great power comes great responsibility", le traitement parallèle ne déroge pas à cette règle et aussi puissante que soit la programmation GPU, elle apporte un cortège de contraintes et de restrictions.
Pour fonctionner en parallèle, il faut que chaque thread soit indépendant des autres. On dit que le thread est aveugle à ce que font les autres threads. Cette restriction implique que toutes les données doivent aller dans le même sens. Il est donc impossible de vérifier le résultat d’un autre thread, de modifier les données d’entrée ou de transmettre le résultat d’un thread à un autre. Autoriser la communication entre threads au moment de l'exécution pourrait compromettre l'intégrité des données en cours de traitement.
Il faut également savoir que le GPU s'assure que tous ses microprocesseurs (les tuyaux) sont actifs en permanence; dès qu'un thread a fini son traitement, le GPU lui ré-assigne une opération à traiter. Le thread ne garde aucune trace de ce qu'il faisait la fois précédente. S'il était en train de dessiner le pixel d'un bouton sur une interface graphique, sa tâche suivante pourra être de rendre un bout du ciel dans un jeu, puis de rendre un bout de texte sur un mail. Donc un thread est non seulement aveugle mais aussi amnésique. En plus du niveau d'abstraction requis pour coder une fonction générique qui saura rendre une image entière uniquement à partir de la variation d'une position, les deux contraintes des threads, cécité et amnésie, rendent la programmation de shaders assez impopulaire auprès des programmeurs débutants.
Mais n'ayez crainte ! Au cours des chapitres suivants, nous aborderons les problèmes étape par étape et passerons d'un simple shader à des shaders plus avancés. Si vous lisez ceci dans un navigateur récent, vous apprécierez de pouvoir manipuler les exemples interactifs et beaucoup de ce qui vient d'être dit deviendra plus clair. Sans plus attendre, jetons nous dans le vif du sujet.
Appuyez sur Next >> pour plonger dans le code !
---
01/README Id (01/README-id.md)
Memulai
Apa itu fragment shader?
Di bab sebelumnya kita menggambarkan shader sebagai persamaan dari mesin cetak Gutenberg untuk grafis. Mengapa? dan yang paling penting: apa itu shader?
Jika anda sudah memiliki pengalaman membuat gambar dengan komputer, anda tau bahwa dalam proses itu anda menggambar suatu lingkaran, lalu segi empat, garis, beberapa segitiga sampai anda menyusun gambar yang anda inginkan. Proses tersebut sangatlah sama dengan menulis sebuah huruf atau buku oleh tangan - itu adalah sekumpulan instruksi yang melakukan satu tugas demi tugas lainnya.
Shader juga merupakan sekumpulan instruksi, tetapi instruksi tersebut dijalankan sekaligus untuk setiap piksel di layar. Artinya, kode yang Anda tulis harus berperilaku berbeda bergantung pada posisi piksel di layar. Seperti jenis pres, program Anda akan berfungsi sebagai fungsi yang menerima posisi dan mengembalikan warna, dan saat dikompilasi, program akan berjalan sangat cepat.
Mengapa shader cepat?
Untuk menjawab ini, saya mempersembahkan keajaiban proses paralel (parallel processing)
Bayangkan CPU di komputermu sebagai pipa industri besar, dan setiap tugas sebagai sesuatu yang melewatinya - seperti jalur pabrik. Beberapa tugas lebih besar dari yang lain, yang berarti mereka membutuhkan lebih banyak waktu dan energi untuk menyelesaikannya. Kami mengatakan mereka membutuhkan lebih banyak daya pemrosesan. Karena arsitektur komputer, pekerjaan terpaksa dijalankan secara seri; setiap pekerjaan harus diselesaikan satu per satu. Komputer modern biasanya memiliki kelompok empat prosesor yang bekerja seperti pipa ini, menyelesaikan tugas satu demi satu agar semuanya berjalan lancar. Setiap pipa juga dikenal sebagai thread.
Permainan video dan aplikasi grafis lainnya membuathkan banyak kekuatan pemrosesan dibandingkan program lain. Karena konten grafis mereka harus melakukan operasi piksel demi piksel yang sangat banyak. Setiap piksel pada layar harus dihitung, dan dalam permainan 3D geometri dan perspektif harus dihitung dengan baik.
Mari kembali ke metafora pipa dan tugas kita. Setiap piksel pada layar mempresentasikan sebuah tugas kecil. Secara individual setiap tugas piksel bukanlah sebuah isu untuk CPI, tapi (dan disinilah masalahnya) tugas kecil harus selesai untuk setiap piksel pada layar! Yang artinya dalam layar 800x600 jadul, 480,000 pixel harus diproses per frame yang artinya 14,400,000 perhitungan per detik! Ya! Itu masalah yang cukup besar untuk membebani mikroprosesor secara berlebihan. Dalam layar retina 2880x1800 modern yang berjalam 60 frame per detik perhitungan tersebut menjadi 311,040,000 perhitungan per detik. Bagaimanakah insinyur grafis memecahkan masalah ini?
Inilah saat proses paralel menjadi solusi terbaik, alih-alih memiliki beberapa mikroprosesor yang besar dan kuat, atau pipa, akan lebih cerdas jika memiliki banyak mikroprosesor kecil yang berjalan secara paralel pada waktu yang sama. Itulah yang dimaksud dengan Graphic Processor Unit (GPU).
Bayangkan mikroprosesor kecil sebagai tabel pipa, dan data setiap piksel sebagai bola pingpong. 14.400.000 bola pingpong per detik dapat menghalangi hampir semua pipa. Tetapi tabel pipa kecil berukuran 800x600 yang menerima 30 gelombang 480.000 piksel per detik dapat ditangani dengan mulus. Ini berfungsi sama pada resolusi yang lebih tinggi - semakin banyak perangkat keras paralel yang Anda miliki, semakin besar aliran yang dapat dikelola.
"Kekuatan super" lain dari GPU adalah fungsi matematika khusus yang dipercepat melalui perangkat keras, sehingga operasi matematika yang rumit diselesaikan secara langsung oleh microchip dan bukan oleh perangkat lunak. Itu berarti operasi trigonometri dan matriks ekstra cepat - secepat listrik bisa berjalan.
Apa itu GLSL?
GLSL kependekan dari OpenGL Shading Language, yang mana adalah spesifik standar dari program shader yang akan anda lihat dalam bab-bab berikutnya. Ada jenis shader lainnya bergantung pada perangkat keras dan sistem operasi. Di sini kita akan bekerja dengan spesifikasi openGL yang diatur oleh Khronos Group. Memahami sejarah OpenGL dapat membantu untuk memahami sebagian besar konvensi anehnya, untuk itu saya sarankan untuk melihat: openglbook.com/chapter-0-preface-what-is-opengl.html
Mengapa Shader terkenal menyakitkan?
Seperti kata paman Ben "dengan kekuatan yang besar, muncul tanggung jawab yang besar" dan perhitungan paralel mengikuti aturan ini; desain arsitektur GPU yang kuat hadir dengan batasan dan batasannya sendiri.
Agar dapat berjalan secara paralel, setiap pipa, atau thread, harus independen dari setiap thread lainnya. Kami mengatakan bahwa thread buta terhadap apa yang dilakukan thread lainnya. Batasan ini menyiratkan bahwa semua data harus mengalir ke arah yang sama. Jadi, tidak mungkin untuk memeriksa hasil thread lain, mengubah data masukan, atau meneruskan hasil thread ke thread lain. Mengizinkan komunikasi thread-to-thread membuat integritas data berisiko.
Juga GPU membuat mikro-prosesor paralel (pipa) terus-menerus sibuk; segera setelah mendapatkan bebas, mereka menerima informasi baru untuk diproses. Tidak mungkin untaian mengetahui apa yang dilakukannya di momen sebelumnya. Itu bisa berupa menggambar tombol dari UI sistem operasi, lalu merender sebagian langit dalam game, lalu menampilkan teks email. Setiap thread tidak hanya buta tetapi juga tanpa memori. Selain abstraksi yang diperlukan untuk mengkodekan fungsi umum yang mengubah hasil piksel demi piksel tergantung pada posisinya, batasan buta dan tanpa memori membuat shader tidak terlalu populer di kalangan programmer pemula.
Jangan khawatir! Pada bab-bab berikut, kita akan mempelajari langkah demi langkah bagaimana beralih dari komputasi bayangan sederhana ke lanjutan. Jika Anda membaca ini dengan browser modern, Anda akan senang bermain dengan contoh interaktif. Jadi jangan tunda lagi keseruannya dan tekan Next >> untuk beralih ke kode!
---
01/README It (01/README-it.md)
Introduzione
Che cosa è un fragment shader?
Nel capitolo precedente abbiamo descritto lo shader come l'equivalente della stampa di Gutenberg per la grafica. Perché? O meglio ancora: che cosa è uno shader?
Se hai già fatto disegni al computer, saprai che si inizia disegnando un cerchio, poi un rettangolo, una linea, alcuni triangoli fino a comporre l'immagine desiderata. Questo processo è molto simile a scrivere una lettera o un libro a mano - in altre parole è un insieme di istruzioni, che eseguono un compito uno dopo l'altro.
Gli shaders si possono considerare allo stesso modo un insieme di istruzioni, che sono eseguite tutte nello stesso momento per ogni singolo pixel sullo schermo. Ciò significa che il codice scritto deve comportarsi in modo diverso a seconda della posizione del pixel sullo schermo. Come nel processo della pressa tipografica, il tuo programma riceverà una posizione e restituirà un colore. Quando il codice è compilato, verrà eseguito a velocità straordinaria.
Perché gli shaders sono così veloci?
Per rispondere a questa domanda, vi presento le meraviglie del calcolo parallelo.
Immaginate la CPU del computer come un grande tubo industriale, e ogni compito come qualcosa che passa attraverso di esso - come in una linea di produzione. Alcuni compiti sono più grandi di altri, il che significa che richiedono più tempo ed energia. Diciamo che necessitano una maggiore potenza di elaborazione. A causa dell'architettura dei computer alcuni compiti sono costretti ad essere eseguiti in serie; ogni compito deve essere completato, uno dopo l'altro. I computer moderni hanno solitamente gruppi di quattro processori che funzionano come questi tubi, eseguendo i compiti uno dopo l'altro per avere le cose funzionanti senza intoppi. Ogni tubo è anche conosciuto come thread.
I videogiochi e le altre applicazioni grafiche richiedono molta più potenza di elaborazione rispetto ad altri programmi. A causa del loro contenuto grafico devono fare un gran numero di operazioni per pixel. Ogni singolo pixel sullo schermo deve essere calcolato, e nei giochi 3D le geometrie e le prospettive devono essere calcolate allo stesso tempo.
Torniamo alla nostra metafora dei tubi e dei compiti. Ogni pixel sullo schermo rappresenta un piccolo compito da realizzare. Individualmente ogni compito non è un problema per la CPU, ma (e qui sta il problema) il piccolo compito deve essere fatto per ogni pixel sullo schermo! Ciò significa che in un vecchio schermo 800x600, 480000 pixel devono elaborati per frame cioè 14400000 calcoli al secondo! Si! Questo è un problema abbastanza grande capace di sovraccaricare un microprocessore. In un moderno schermo retina 2880x1800 in esecuzione a 60 fotogrammi al secondo si possono raggiungere fino a 311040000 calcoli al secondo. Come fanno gli ingegneri grafici a risolvere questo problema?
E' in questo caso che il calcolo parallelo diventa una buona soluzione. Invece di avere un paio di grandi e potenti microprocessori, o tubi, è più intelligente avere parecchi minuscoli microprocessori che funzionano in parallelo allo stesso tempo. Questo è la Graphic Processor Unit (GPU).
Immaginate i minuscoli microprocessori come un tavolo di tubi, ed i dati di ciascun pixel come una pallina da ping pong. 14400000 palline da ping pong in un secondo possono ostacolare qualsiasi tubo. Ma un tavolo di 800x600 piccoli tubi che riceve 30 ondate di 480000 pixel al secondo si può gestire senza problemi. Tutto ciò funziona anche a risoluzioni più elevate - più si possiede hardware in parallelo, più grande è il flusso che si può gestire.
Un altro "super potere" della GPU è che alcune speciali funzioni matematiche sono accelerate via hardware, in modo tale che le operazioni matematiche complesse sono risolte direttamente dai microchip invece che da un software. Ciò significa che avremo una velocità extra nel calcolo trigonometrico e nelle operazioni fra matrici, una velocità pari a quella dell'elettricità.
Che cosa è GLSL?
GLSL è l'acronimo di openGL Shading Language, che è lo standard specifico dei programmi shaders che vedrete nei prossimi capitoli. Ci sono altri tipi di shaders a seconda del hardware e dei sistemi operativi. Qui lavoreremo con le specifiche openGL regolate dal Khronos Group. Comprendere la storia di OpenGL può essere utile per capire la maggior parte delle sue strane convenzioni, per questo vi consiglio di dare un'occhiata a: openglbook.com/chapter-0-preface-what-is-opengl.html
Perché gli Shaders hanno notoriamente una brutta fama?
Come disse lo zio Ben "con un grande potere derivano grandi responsabilità", e il calcolo parallelo segue questa regola; la potente progettazione architettonica della GPU comporta specifici vincoli e restrizioni.
Affinché ogni tubo, o thread, venga eseguito in parallelo, è necessario che sia indipendente l'uno dall'altro. Diciamo i thread sono ciechi e non vedono ciò che gli altri thread stanno facendo. Questa restrizione implica che tutti i dati debbano affluire nella stessa direzione. Quindi è impossibile controllare il risultato di un altro thread, modificare i dati di input, o passare il risultato di un thread a un altro thread. Permettere la comunicazione fra thread metterebbe a rischio l'integrità dei dati.
Anche la GPU mantiene costantemente occupati i micro-processore paralleli (i tubi); non appena sono liberi ricevono nuove informazioni da elaborare. E' quindi impossibile per un thread sapere che cosa stava facendo nel momento precedente. Potrebbe disegnare un bottone dell'UI del sistema operativo, subito dopo rendere una porzione di cielo in un videogioco e poi visualizzare il testo di una e-mail. Ogni thread non è solo cieco ma anche senza memoria. Oltre all'astrazione necessaria per programmare una funzione generica che cambia il risultato pixel per pixel, a seconda della sua posizione, i vincoli ciechi e senza memoria non rendono gli shaders molto popolari tra i programmatori debuttanti.
Non preoccuparti! Nei capitoli seguenti, impareremo passo-passo a partire dai semplici shaders fino a quelli avanzati. Se stai leggendo questo libro con un navigatore moderno, potrai giocare con gli esempi interattivi. Quindi non aspettare altro tempo e clicca su Next >> per iniziare il divertimento!
---
01/README Pl (01/README-pl.md)
Początki
Czym jest fragment shader?
W poprzednim rozdziale nazwaliśmy shadery ekwiwalentem prasy drukarskiej Gutenberga dla grafiki. Dlaczego? A co ważniejsze: czym jest shader?
Jeżeli masz doświadczenie w rysowaniu z użyciem komputera (np. w Paint), to wiesz, że proces ten polega na rysowaniu kółek, prostokątów, linii oraz trójkątów do momentu skomponowania pożądanego obrazu. Proces ten jest bardzo podobny do pisania listów lub książek odręcznie - jest to zbiór instrukcji wykonujących zadanie po zadaniu.
Shader również jest zbiorem instrukcji, ale wykonywanych równocześnie dla każdego piksela na ekranie. Oznacza to, że kod, który piszesz musi działać inaczej w zależności od pozycji piksela na ekranie. Podobnie jak maszyna drukarska, twój program będzie działał jak funkcja matematyczna otrzymująca pozycję piksela i zwracająca jego kolor. Po skompilowaniu twój program będzie działał błyskawicznie.
Dlaczego shadery są szybkie?
Aby odpowiedzieć na pytanie, omówmy cud przetwarzania równoległego (ang. parallel processing)
Wyobraź sobie procesor twojego komputera jako potok przetwarzania (ang. "pipeline"), przez który przechodzą różnorakie zadania (jak na linii produkcyjnej w fabryce). Niektóre zadania są większe od innych, co oznacza, że wymagają więcej czasu i energii. Mówimy wtedy, że wymagają więcej mocy obliczeniowej (ang. processing power). Ze względu na architekturę współczesnych komputerów, zadania te wykonują się seryjnie (jeden po drugim) - każde zadanie musi poczekać, dopóki poprzednie zadanie nie zostanie ukończone. Jednakże, współczesne komputery posiadają zwykle więcej niż jedną jednostkę przetwarzającą (np. 2, 4 lub 8 rdzeni procesora), które funkcjonują jak pomniejsze potoki przetwarzania. Każdy taki pomniejszy potok nazywany jest również wątkiem (ang. thread).
Gry wideo i inne aplikacje graficzne wymagają zdecydowanie więcej mocy obliczeniowej niż większość programów, gdyż muszą wykonywać ogromne ilości operacji piksel po pikselu. Nie dość, że każdy pojedynczy piksel musi być obliczony, to w przypadku gier 3D dochodzą do tego obliczenia geometryczne i obliczenie perspektywy.
Wróćmy do naszej metafory potoku przetwarzania. Każdy piksel na ekranie reprezentuje proste zadanie. Indywidualnie zadania te nie stanowią problemu dla CPU, jednak sytuacja zmienia się, gdy takie zadanie musi być wykonane dla każdego piksela na ekranie. Oznacza to, że na starym monitorze 800x600 na jedną klatkę przypada 480.000 obliczeń, co oznacza 14.400.000 obliczeń na jedną sekundę! Właśnie tak! Skala problemu może przeciążyć mikroprocesor. Co więcej, na współczesnym monitorze 2560x1440 przy 60 FPS osiągamy 221.356.800 obliczeń na sekundę. Jak inżynierowie graficzni rozwiązują ten problem?
Z pomocą przychodzi przetwarzanie równoległe. Zamiast kilku dużych, potężnych mikroprocesorów (potoków) lepiej mieć wiele małych mikroprocesorów działających równolegle. Tak właśnie działa procesor graficzny (GPU) w karcie graficznej.
Wyobraź sobie mały mikroprocesor jako tablicę rur (spójrz na obrazek powyżej), a dane jako piłeczki ping pongowe. 14.400.000 piłeczek ping pongowych na sekundę może zablokować prawie każdą pojedynczą rurę. Ale tabela 800x600 malutkich rur przyjmująca co sekundę 30 fal po 480.000 piłeczek poradzi sobie z nimi bez problemu. Tak samo działa to na wyższych rozdzielczościach - im więcej równolegle pracującego hardware'u, tym większy potok, z którym GPU sobie poradzi.
Inną "super umiejętnością" GPU jest fakt, że złożone funkcje matematyczne wykonywane są bezpośrednio na poziomie hardware'u przez mikroczipy, a nie przez software. Skutkiem tego są super szybkie operacje trygonometryczne i macierzowe.
Czym jest GLSL?
GLSL oznacza "OpenGL Shading Language" i stanowi standard pisania shaderów, który zobaczysz w następnych rozdziałach tej książki. W zależności od hardware'u i systemu operacyjnego wyróżnia się też inne rodzaje shaderów. Tutaj skupimy się na specyfikacji OpenGL uregulowanej przez Khronos Group. Zrozumienie historii OpenGL może pomóc w zrozumieniu wielu dziwnych konwencji; w tym celu polecam zajrzeć do: openglbook.com/chapter-0-preface-what-is-opengl.html.
Dlaczego shadery budzą postrach?
Jak to mówią: "with great power comes great responsibility". Stosuje się to również do obliczeń równoległych - potężne rozwiązania architektoniczne w GPU wiążą się również z pewnymi ograniczeniami.
Aby wątki mogły działać równolegle, muszą być od siebie niezależne. Mówimy, że wątki są ślepe na to, co robi reszta wątków. Ograniczenie to implikuje, że dane muszą "płynąć w tę samą stronę" - nie jest możliwe sprawdzić dane wyjściowe innego wątku, zmodyfikować jego dane wejściowe albo przekazać dane wyjściowe jednego wątku jako dane wejściowe innego.
Poza tym GPU odpowiada za to, żeby każdy wątek miał coś do roboty, i żeby otrzymał dane potrzebne do wykonania tej roboty. Trzeba też pamiętać, że nie jest możliwe, aby wątek wiedział, co robił sekundę temu - mógł rysować przycisk w UI systemu operacyjnego, a potem renderować fragment nieba w grze wideo, a jeszcze potem wyświetlać treść maila. Każdy wątek jest nie tylko ślepy, ale również bez pamięci. Te cechy sprawiają, że pisanie shaderów nie cieszy się dużą popularnością wśród początkujących programistów.
Ale nie martw się! W następnych rozdziałach nauczymy się, krok po kroku, prostych i zaawansowanych obliczeń shadingowych. Jeżeli czytasz to we współczesnej przeglądarce, to z pewnością docenisz zabawę z interaktywnymi przykładami. Ale nie przedłużajmy! Naciśnij Next>> aby przejść dalej.
---
01/README Pt (01/README-pt.md)
Começando
O que é um fragment shader?
No capítulo anterior descrevemos shaders como o equivalente dos avanços trazidos pela prensa de Gutenberg. Por que? E mais importante: o que é um shader?
Se você já tem experiência em desenhar em computadores, você sabe que nesse processo você desenha um círculo, depois um retângulo, uma linha, alguns triângulos até você finalmente compor a imagem desejada. O processo é muito similar a escrever uma carta ou livro à mão, um conjunto de instruções que executam uma tarefa atrás de outra.
Shaders são também um conjunto de instruções, mas as instruções são executadas todas ao mesmo tempo para cada pixel da tela. Isso significa que o código que você escreve têm que se comportar de maneira diferente, dependendo da posição do pixel na tela. Como uma prensa tipográfica, seu programa funcionará como uma função que recebe uma posição e retorna a respectiva cor, e, quando esse programa é compilado, ele será executado extraordinariamente rápido.
Por que shaders são rápidos?
Para responder essa pergunta, eu apresento as maravilhas do processamento paralelo.
Imagine a CPU do seu computador como um grande tubo (ou ducto) industrial, e cada tarefa como algo que passa por ele - como uma linha de produção. Algumas tarefas são maiores que outras, o que signica que elas requerem mais tempo e energia que as outras. Digamos que elas requerem mais poder de processamento. Por causa da arquitetura dos computadores, as tarefas são forçadas a serem executadas em série; cada tarefa é finalizada uma de cada vez. Computadores modernos geralmente tem grupos de quatro processadores para trabalhar com tais tubos, completando as tarefas sequencialmente e mantendo a linha de produção em movimento. Cada tubo é também conhecido como thread.
Video games e outras aplicações gráficas requerem muito mais poder de processamento que outros programas. Por causa de seu conteúdo gráfico, eles precisam fazer um número enorme de operações pixel-por-pixel. Cada pixel da tela precisa ser computado, e em jogos em 3D geometrias e perspectivas também precisam ser calculadas.
Vamos voltar para a nossa metáfora de tubos e tarefas. Cada pixel da tela representa uma tarefa pequena e simples. Individualmente, cada tarefa deste tipo não é um problema para o CPU, mas (e aqui mora o perigo) a pequena tarefa precisa ser repetida para cada pixel na tela! Isso significa que em uma tela antiga de 800x600, 480.000 pixels precisam ser processados pada cada frame, o que totaliza em 14.400.000 cálculos por segundo! Sim! Isso é um problema grande o suficiente para sobrecarregar um microprocessador. Em uma tela moderna com retina display medindo 2880x1800, executando a 60 frames por segundo, esse cálculo resulta em até 311.040.000 cálculos por segundo. Como engenheiros gráficos resolvem esse problema?
Neste caso, processamento em paralelo se torna uma boa solução. Em vez de ter alguns microprocessadores grandes e poderosos, ou tubos, é mais inteligente ter um monte de minúsculos microprocessadores rodando em paralelo ao mesmo tempo. Isso é o que a Unidade de Processamento Gráfico (Graphic Processor Unit ou GPU) é.
Imagine os minúsculos microprocessadores como uma mesa de tubos, e os dados de cada pixel como uma bola de ping-pong. 14.400.000 bolas de ping-pong por segundo podem obstruir quase qualquer tubo. Mas a mesa com 800x600 pequenos tubos recebendo 30 ondas de 480.000 pixels por segundo pode manejar esse fluxo tranquilamente. Funciona do mesmo jeito em resoluções maiores - quanto mais hardware em paralelo você tem, maior o fluxo que eles podem manejar.
Outro "super poder" da GPU são funções matemáticas especiais aceleradas via hardware, logo operações matemáticas mais complicadas são resolvidas diretamente pelos microchips em vez do software. Isso resulta em operações trigonométricas e matriciais extra rápidas - tão rápidas quanto a eletricidade.
O que é GLSL?
GLSL é a sigla de openGL Shading Language, o que é o padrão específico de shader que você verá nos próximos capítulos. Existem outros tipos de shaders, dependendo do hardware ou sistemas operacionais. Aqui trabalharemos com as especificações do openGL, reguladas por Khronos Group. Entender a história do OpenGL pode ser útil se compreender a maior parte das estranhas convenções, para isso eu te recomendo a dar uma olhada em: openglbook.com/chapter-0-preface-what-is-opengl.html
Por que shaders têm uma má reputação?
Como disse o Tio Ben “com grandes poderes vêm grandes responsabilidades” e computação em paralelo segue essa regra; o poderoso design aquitetônico da GPU vem com suas próprias limitações e restrições.
Para cada tubo - ou thread - ser executado em paralelo, eles têm que ser independentes de outras threads. Podemos dizer que threads são cegas com relação ao que as outras threads estão fazendo. Essa restrição faz com que todos os dados devem fluir na mesma direção. Deste modo é impossível checar o resultado de outra thread, modificar o input de dados ou passar o resultado de uma thread para outra. Permitir comunicação entre as threads coloca a integridade dos dados em risco.
Além disso, a GPU mantém os microprocessadores (os tubos) constantemente ocupados; assim que eles ficam livres eles recebem novas informações para serem processadas. É impossível para uma thread saber o que ela estava fazendo num momento anterior. Isso poderia ser desenhar um botão para a UI do sistema operacional, depois renderizar uma porção do céu em um jogo, seguido de mostrar o texto de um email. Cada thread não é só cega como sem memória. Apesar da abstração requerida para programar uma função genérica que muda o resultado pixel por pixel dependendo da posição do mesmo, as limitações desta cegueira e falta de memória faz com que os shaders não sejam muito populares entre programadores iniciantes.
Não se preocupe! Nos capítulos seguintes aprenderemos passo-a-passo a trabalhar com shaders com exemplos que vão de simples a avançados. Se você está lendo este livro em um navegador moderno, você pode poderá brincar com os exemplos interativos. Então clique em Next >> e mãos à obra!
---
01/README Tr (01/README-tr.md)
Başlarken
Fragment shader nedir?
Önceki bölümde shader'ları, grafikler için Gutenberg matbaasının eşdeğeri olarak tanımlamıştık. Neden? Ve daha da önemlisi: shader nedir?
Bilgisayarla çizim yapma konusunda zaten deneyiminiz varsa, bu süreçte istediğiniz görüntüyü oluşturana kadar bir daire, sonra bir dikdörtgen, bir çizgi, bazı üçgenler çizdiğinizi bilirsiniz. Bu süreç, elle mektup veya kitap yazmaya çok benzer - birbiri ardına tek bir görevi yerine getiren talimatlar kümesidir.
Shader'lar da bir talimatlar kümesidir, ancak talimatlar ekrandaki her bir piksel için aynı anda yürütülür. Bu, yazdığınız kodun pikselin ekrandaki konumuna bağlı olarak farklı davranması gerektiği anlamına gelir. Bir matbaa baskısı gibi, programınız bir konum alan ve bir renk döndüren bir fonksiyon olarak çalışacak ve derlendiğinde olağanüstü hızlı çalışacaktır.
Shader'lar neden hızlıdır?
Bunu cevaplamak için size paralel işlemenin mucizelerini sunuyorum.
Bilgisayarınızın CPU'sunu büyük bir endüstriyel boru ve her görevi de içinden geçen bir şey olarak hayal edin - bir fabrika hattı gibi. Bazı görevler diğerlerinden daha büyüktür, bu da onlarla başa çıkmak için daha fazla zaman ve enerji gerektirdiği anlamına gelir. Daha fazla işlem gücü gerektirdiklerini söyleriz. Bilgisayarların mimarisi nedeniyle işler bir seri halinde çalışmaya zorlanır; her işin teker teker bitirilmesi gerekir. Modern bilgisayarlar genellikle bu borular gibi çalışan dört işlemci grubuna sahiptir ve işlerin sorunsuz yürümesini sağlamak için görevleri birbiri ardına tamamlarlar. Her boru aynı zamanda bir iş parçacığı (thread) olarak da bilinir.
Video oyunları ve diğer grafik uygulamaları, diğer programlardan çok daha fazla işlem gücü gerektirir. Grafik içerikleri nedeniyle çok sayıda piksel bazlı işlem yapmak zorundadırlar. Ekrandaki her bir pikselin hesaplanması gerekir ve 3D oyunlarda geometrilerin ve perspektiflerin de hesaplanması gerekir.
Borular ve görevler metaforumuza geri dönelim. Ekrandaki her piksel basit ve küçük bir görevi temsil eder. Bireysel olarak her piksel görevi CPU için bir sorun değildir, ancak (ve işte sorun burada) bu küçük görevin ekrandaki her piksel için yapılması gerekir! Bu, eski bir 800x600 ekranda kare başına 480.000 pikselin işlenmesi gerektiği, yani saniyede 14.400.000 hesaplama anlamına gelir! Evet! Bu, bir mikroişlemciyi aşırı yükleyecek kadar büyük bir sorundur. Saniyede 60 kare hızında çalışan modern bir 2880x1800 retina ekranda bu hesaplama saniyede 311.040.000 hesaplamaya ulaşır. Grafik mühendisleri bu sorunu nasıl çözüyor?
İşte bu noktada paralel işleme iyi bir çözüm haline gelir. Birkaç büyük ve güçlü mikroişlemciye veya boruya sahip olmak yerine, aynı anda paralel çalışan çok sayıda küçük mikroişlemciye sahip olmak daha akıllıcadır. Grafik İşlemci Birimi (GPU) işte budur.
Küçük mikroişlemcileri bir boru masası ve her pikselin verisini bir ping pong topu olarak hayal edin. Saniyede 14.400.000 ping pong topu neredeyse her boruyu tıkayabilir. Ancak saniyede 480.000 piksellik 30 dalgayı alan 800x600 küçük borudan oluşan bir masa sorunsuz bir şekilde idare edilebilir. Bu, daha yüksek çözünürlüklerde de aynı şekilde çalışır - ne kadar çok paralel donanımınız varsa, o kadar büyük bir akışı yönetebilir.
GPU'nun bir diğer “süper gücü” de donanım üzerinden hızlandırılan özel matematik fonksiyonlarıdır, böylece karmaşık matematik işlemleri yazılım yerine doğrudan mikroçipler tarafından çözülür. Bu, ekstra hızlı trigonometrik ve matris işlemleri anlamına gelir - elektriğin gidebileceği kadar hızlı.
GLSL nedir?
GLSL, OpenGL Shading Language (OpenGL Gölgelendirme Dili) anlamına gelir ve sonraki bölümlerde göreceğiniz shader programlarının özel standardıdır. Donanım ve İşletim Sistemlerine bağlı olarak başka shader türleri de vardır. Burada Khronos Group tarafından düzenlenen OpenGL spesifikasyonlarıyla çalışacağız. OpenGL'in tarihini anlamak, garip kurallarının çoğunu anlamak için yararlı olabilir, bunun için şuna bir göz atmanızı öneririm: openglbook.com/chapter-0-preface-what-is-opengl.html
Shader'lar neden meşhur derecede acı vericidir?
Ben Amca'nın dediği gibi “büyük güç büyük sorumluluk getirir” ve paralel hesaplama da bu kurala uyar; GPU'nun güçlü mimari tasarımı kendi kısıtlamaları ve sınırlamalarıyla birlikte gelir.
Paralel çalışabilmek için her borunun veya iş parçacığının diğer tüm iş parçacıklarından bağımsız olması gerekir. İş parçacıklarının diğer iş parçacıklarının ne yaptığına kör olduğunu söyleriz. Bu kısıtlama, tüm verilerin aynı yönde akması gerektiği anlamına gelir. Bu nedenle başka bir iş parçacığının sonucunu kontrol etmek, girdi verilerini değiştirmek veya bir iş parçacığının sonucunu başka bir iş parçacığına aktarmak imkansızdır. İş parçacıklarının birbirleriyle iletişim kurmasına izin vermek verilerin bütünlüğünü riske atar.
Ayrıca GPU, paralel mikro işlemcileri (boruları) sürekli meşgul tutar; serbest kalır kalmaz işlemek için yeni bilgiler alırlar. Bir iş parçacığının bir önceki anda ne yaptığını bilmesi imkansızdır. İşletim sisteminin arayüzünden bir düğme çiziyor, sonra bir oyunda gökyüzünün bir bölümünü oluşturuyor, sonra bir e-postanın metnini görüntülüyor olabilir. Her iş parçacığı sadece kör değil, aynı zamanda hafızasızdır. Konumuna bağlı olarak sonucu piksel piksel değiştiren genel bir fonksiyon kodlamak için gereken soyutlamanın yanı sıra, kör ve hafızasız kısıtlamalar shader'ları başlangıç seviyesindeki programcılar arasında pek popüler kılmaz.
Endişelenmeyin! Sonraki bölümlerde, basit gölgelendirme hesaplamalarından ileri seviye hesaplamalara adım adım nasıl geçileceğini öğreneceğiz. Bunu modern bir tarayıcıyla okuyorsanız, interaktif örneklerle oynamaktan keyif alacaksınız. Öyleyse eğlenceyi daha fazla ertelemeyelim ve koda atlamak için Sonraki >> düğmesine basalım!
---
01/README Ua (01/README-ua.md)
Вступ
Що таке фрагментний шейдер?
У попередньому розділі ми описали шейдери як еквівалент пресу Гутенберга для графіки. Чому? І що ще важливіше: що таке шейдер?
Якщо у вас уже є досвід малювання за допомогою комп'ютера, то ви знаєте, що в цьому процесі ви, умовно кажучи, спочатку малюєте коло, потім прямокутник, лінію, ще кілька трикутників і так далі, доки не отримаєте потрібне зображення. Цей процес дуже схожий на написання листа чи книги від руки – це набір інструкцій, які послідовно виконують одне завдання за іншим.
Шейдери також є набором інструкцій, але вони виконуються одночасно для кожного пікселя на екрані. Це означає, що код, який ви пишете, має поводити себе по-різному залежно від положення пікселя на екрані. Подібно до печатного преса, ваша програма працюватиме як функція, яка отримує координати пікселя та повертає колір. Після компіляції вона працюватиме надзвичайно швидко.
Чому шейдери швидкі?
Щоб відповісти на це питання, я розповім про чудеса паралельних обчислень.
Уявіть центральний процесор вашого комп'ютера у вигляді великої промислової труби, а кожне завдання як щось, що проходить крізь неї, як через фабричну лінію. Деякі завдання більші за інші, а це означає, що для їх вирішення потрібно більше часу та енергії. В комп'ютерному сенсі ми скажемо, що їм потрібна більша обчислювальна потужність. Через особливості архітектури комп'ютерів завдання виконуються послідовно, тобто по черзі. Сучасні комп'ютери зазвичай мають групи з кількох процесорів, які працюють як ці труби, виконуючи завдання одне за одним, щоб забезпечити безперебійну роботу. Кожна подібна труба також називається потоком.
Відеоігри та інші графічні програми вимагають набагато більшої обчислювальної потужності, ніж інші програми. Через свій графічний контент їм доводиться виконувати величезну кількість попіксельних операцій. Необхідно обчислити кожен окремий піксель на екрані, а в 3D-іграх також потрібно обчислити усю геометрію і перспективу.
Повернемося до нашої метафори про труби та завдання. Кожен піксель на екрані представляє просте маленьке завдання. Окремо такі завдання не є проблемою для CPU, але проблема в тому, що кожне крихітне завдання потрібно виконати для кожного пікселя на екрані! Це означає, що на старому екрані з роздільною здатністю 800x600 потрібно обробляти 480 000 пікселів на 1 кадр оновлення, що становить близько 14 400 000 обчислень за секунду! О, так! Це достатньо велика проблема, щоб перенавантажити мікропроцесор. У сучасному дисплеї Retina 2880x1800, що оновлюється зі швидкістю 60 кадрів на секунду, ця кількість обчислень за секунду складе 311 040 000. Як інженери графічних систем розв'язують цю проблему?
Ось коли паралельні обчислення стають хорошим рішенням. Замість того, щоб мати пару великих і потужних мікропроцесорів, або труб, розумніше мати багато маленьких мікропроцесорів, що працюють паралельно. Саме так і влаштовано графічний процесор (GPU).
Уявіть крихітні мікропроцесори у вигляді столу із труб, а дані кожного пікселя як кульку для пінг-понгу. 14 400 000 кульок для пінг-понгу за секунду можуть закупорити майже будь-яку трубу. Але стіл з крихітними трубками розміром 800x600, зможе спокійно прийняти 30 хвиль по 480 000 пікселів за секунду і працюватиме безперебійно. Те ж саме і з прикладом вищої роздільної здатності - чим більше у вас обладнання, що працює паралельно, тим більшим потоком воно зможе керувати.
Ще одна "суперздатність" графічного процесора — це спеціальні математичні функції, прискорені за допомогою апаратного забезпечення. Тож складні математичні операції вирішуються безпосередньо мікрочіпами, а не програмним забезпеченням. Це означає надшвидкі тригонометричні та матричні операції - настільки швидкі, наскільки швидко може рухатися електрика.
Що таке GLSL?
GLSL розшифровується як OpenGL Shading Language і є спеціальним стандартом шейдерних програм, які ви побачите в наступних розділах. Залежно від апаратного забезпечення та операційної системи існують різні типи шейдерів. Ми працюватимемо зі специфікаціями OpenGL, які регулює Khronos Group. Розуміння історії OpenGL може бути корисним для розуміння більшості його дивних конвенцій. Для цього я рекомендую переглянути наступне посилання: openglbook.com/chapter-0-preface-what-is-opengl.html
Чому шейдери такі болючі?
Як сказав дядько Бен, "з великою силою приходить велика відповідальність" і паралельні обчислення дотримуються цього правила. Потужний архітектурний дизайн графічного процесора має власні обмеження.
Щоб працювати паралельно, кожен канал або потік має бути незалежним від будь-якого іншого потоку. Ми кажемо, що потоки сліпі стосовно того, що роблять інші потоки. Це обмеження означає, що всі дані повинні рухатись в одному напрямку. Тому неможливо перевірити результат іншого потоку, змінити вхідні дані або передати результат одного потоку в інший потік. Спроба дозволу міжпотокових зв'язків ставить під загрозу цілісність даних.
Крім того, GPU підтримує свої мікропроцесори (труби) постійно зайнятими. Як тільки вони звільняються, вони отримують нову інформацію для обробки. Потоку неможливо дізнатися, що він робив у попередній момент. Це могло бути малювання кнопки інтерфейсу операційної системи, потім рендеринг частини неба із гри, а потім зображення тексту електронного листа. Кожен потік є не лише сліпим, а й безпам'ятним. Окрім абстракції, необхідної для кодування загальної функції, яка змінює результат в залежності від положення пікселя, сліпота і безпам'ятство роблять шейдери не дуже популярними серед програмістів-початківців.
Не хвилюйтесь! У наступних розділах ми крок за кроком розглянемо шейдерні обчислення від простого до складного. Якщо ви читаєте книгу в сучасному браузері, то оціните можливість взаємодії з інтерактивними прикладами. Тож давайте більше не відкладати веселощі та натискайте на посилання Next >>, щоб перейти до програмування!
---
01/README Vi (01/README-vi.md)
Mở đầu
Fragment shader là gì?
Ở chương trước, tôi đã mô tả shader tương đương với cỗ máy in của Gutenberg nhưng cho ngành đồ hoạ. Tại sao? Và quan trọng hơn: shader là gì?
Nếu bạn đã từng vẽ trên máy tính, bạn biết rằng để có được hình mình muốn, bạn phải vẽ hình tròn, hình chữ nhật rồi vài đường thẳng, vài hình tam giác. Quá trình đó không khác gì việc viết từng chữ một - đó là một loạt các chỉ dẫn để máy tính thực hiện lần lượt.
Shader cũng là một tập hợp các chỉ dẫn, nhưng các chỉ dẫn đó sẽ được thực thi cùng lúc cho từng điểm ảnh trên màn hình. Điều đó có nghĩa là code bạn viết phải xử lý khác nhau tuỳ theo vị trí của điểm ảnh trên màn hình. Giống như máy rập chữ, chương trình của bạn sẽ hoạt động như một hàm nhận vị trí của điểm ảnh rồi trả về màu của điểm ảnh đó. Chương trình đó chạy rất rất nhanh.
Làm cách nào shader lại chạy nhanh tới vậy?
Để trả lời câu hỏi này, tôi xin giới thiệu với bạn sự kỳ diệu của việc xử lý song song.
Hãy tưởng tượng CPU là một dây chuyền công nghiệp lớn, và mọi tác vụ đi qua dây chuyền là một khâu. Có vài khâu đồ sộ hơn các khâu khác, tức là chúng cần nhiều thời gian và năng lượng hơn để xử lý. Ta nói chúng cần nhiều năng lực xử lý hơn. Kiến trúc của máy tính khiến mỗi khâu phải thực hiện tuần tự; khâu này kết thúc rồi mới đến khâu tiếp theo. Máy tính hiện đại thường có tới 4 bộ xử lý tương tự như 4 dây chuyền sản xuất này, lần lượt thực thi từng tác vụ nhỏ. Mỗi dây chuyền nhỏ trong đó gọi là một thread.
Các trò chơi điện tử và các ứng dụng đồ hoạ cần nhiều năng lực xử lý hơn hẳn các phần mềm khác. Vì các nội dung đồ hoạ của chúng yêu cầu thực hiện rất nhiều phép toán, cho từng điểm ảnh một. Mỗi điểm ảnh trên màn hình đều phải được tính đến, còn trong các trò chơi 3 chiều thì cả các vật thể lẫn các góc camera cũng phải được tính luôn.
Quay trở lại phép so sánh về dây chuyền và tác vụ. Mỗi điểm ảnh trên màn hình đại diện cho 1 tác vụ nhỏ. Bản thân từng tác vụ không phải là vấn đề lớn với CPU, nhưng (vấn đề ở đây là) các tác vụ bé xíu này lại phải được thực thi cho từng điểm ảnh trên cả màn hình. Có nghĩa là trên màn hình cũ có độ phân giải 800x600, có tới 480.000 điểm ảnh cần phải được xử lý mỗi khung hình, tương đương với 14.400.000 phép tính mỗi giây! Đúng thế! Đó chính là điều khiến bộ vi xử lý bị quá tải. Còn ở màn hình retina thời hiện đại có độ phân giải 2880x1800 hiển thị 60 khung hình một giây, thì số phép tính mỗi giây lên tới 311.040.000. Bằng cách nào mà các kỹ sư đồ hoạ giải quyết được vấn đề này?
Đây là lúc mà xử lý song song trở thành một giải pháp tốt. Thay vì phải trang bị vài bộ vi xử lý to lớn và mạnh mẽ, hoặc các dây chuyền, sẽ là thông minh hơn nếu để cho hàng loạt các bộ vi xử lý tí hon chạy song song. Và đó chính là việc mà các bộ xử lý đồ hoạ (GPU) làm.
Hình dung các bộ vi xử lý tí hon là hàng loạt những ống xếp dọc và kết lại với nhau thành một cái bàn, còn dữ liệu của mỗi điểm ảnh là một quả bóng bàn. Nhét 14.400.000 quả bóng bàn vào một ống đơn lẻ trong một giây sẽ rất khó. Nhưng khi rất nhiều ống xếp thành hàng 800x600 thì có thể đưa 480.000 quả bóng bàn chui qua tới 30 lần một giây một cách dễ dàng. Độ phân giải cao hơn cũng tương tự - phần cứng càng có năng lực xử lý song song thì khối lượng công việc mà nó có thể giải quyết lại lớn hơn.
Một "siêu năng lực" khác của GPU là các hàm Toán học được tối ưu bằng phần cứng, nên các phép toán phức tạp sẽ được xử lý trực tiếp trên phần cứng thay vì phần mềm. Điều đó có nghĩa là các phép tính lượng giác và ma trận sẽ được tính cực kỳ nhanh - như điện luôn.
GLSL là gì?
GLSL là viết tắt của OpenGL Shading Language, là một quy chuẩn để viết các shader mà ta sẽ dùng ở các chương tới. Có nhiều loại shader phụ thuộc vào phần cứng và hệ điều hành. Ở đây chúng ta sẽ sử dụng quy chuẩn của
Khronos Group. Hiểu về lịch sử hình thành của OpenGL sẽ giúp ích trong việc vượt qua được một số rào cản kỳ lạ của nó, vì thế tôi khuyên bạn nên xem qua: openglbook.com/chapter-0-preface-what-is-opengl.html
Shader nổi tiếng khó nhằn, sao lại thế?
Như chú Ben (một nhân vật trong phim Spider Man) nói "Quyền lực càng cao, trách nhiệm càng lớn", và việc tính toán song song cũng tuân thủ quy tắc này; thiết kế kiến trúc rất mạnh mẽ của GPU cũng đi kèm với các ràng buộc và giới hạn.
Để các dây chuyền, tức các thread, có thể chạy song song, thì chúng phải độc lập. Có thể nói rằng các thread bị mù khi không thể biết được các thread khác đang làm gì. Giới hạn này dẫn tới việc toàn bộ dữ liệu phải đi theo 1 chiều. Nên thread này không thể biết kết quả của thread kia hay thay đổi dữ liệu đầu vào hoặc lấy dữ liệu đầu ra của một thread nọ để chuyển cho một thread khác nữa. Việc cho phép các thread liên thông với nhau (thread-to-thread) sẽ gây rủi ro cho tính toàn vẹn của dữ liệu.
Và GPU cũng luôn khiến cho các bộ vi xử lý (các dây chuyền) của mình phải bận rộn; cứ dây chuyền nào xong việc thì sẽ nhận được thông tin mới để xử lý tiếp. Nên mỗi thread còn chẳng biết nó vừa hoàn thành xong việc gì. Nó có thể vừa mới vẽ xong 1 nút bấm trên giao diện của hệ điều hành, rồi vẽ một phần bầu trời trong 1 trò chơi nào đó, sau đó lại phải hiển thị nội dung của 1 cái email. Mỗi thread không chỉ bị mù mà còn mất trí nhớ nữa. Bên cạnh việc viết code shader khá trừu tượng do phải viết một hàm dùng chung cho mọi điểm ảnh nhưng kết quả thì phụ thuộc vào vị trí của điểm ảnh đó, thì các ràng buộc về việc bị mù và mất trí nhớ ở trên cũng là nguyên do khiến cho shader không được biết đền nhiều bởi các lập trình viên mới vào nghề.
Nhưng đừng lo! Ở các chương tới, ta sẽ học từng bước một, từ đơn giản tới phức tạp. Nếu bạn đang đọc trên một trình duyệt đời mới, bạn có thể sẽ thích tương tác với các ví dụ. Đừng trì hoãn sự sung sướng nữa mà hãy click nút Next >> để nhảy thẳng vào code nào!
---
01/TITLE Gr (01/TITLE-gr.md)
Ξεκινώντας
Τί είναι ένας fragment shader (φωτοσκιαστής τεμαχίων);
---
02/README (02/README.md)
Hello World
Usually the "Hello world!" example is the first step to learning a new language. It's a simple one-line program that outputs an enthusiastic welcoming message and declares opportunities ahead.
In GPU-land rendering text is an overcomplicated task for a first step, instead we'll choose a bright welcoming color to shout our enthusiasm!
<div class="codeAndCanvas" data="hello_world.frag"></div>
If you are reading this book in a browser the previous block of code is interactive. That means you can click and change any part of the code you want to explore. Changes will be updated immediately thanks to the GPU architecture that compiles and replaces shaders on the fly. Give it a try by changing the values on line 8.
Although these simple lines of code don't look like a lot, we can infer substantial knowledge from them:
1. Shader Language has a single main function that returns a color at the end. This is similar to C.
2. The final pixel color is assigned to the reserved global variable gl_FragColor.
3. This C-flavored language has built in variables (like gl_FragColor), functions and types. In this case we've just been introduced to vec4 that stands for a four dimensional vector of floating point precision. Later we will see more types like vec3 and vec2 together with the popular: float, int and bool.
4. If we look closely to the vec4 type we can infer that the four arguments respond to the RED, GREEN, BLUE and ALPHA channels. Also we can see that these values are normalized, which means they go from 0.0 to 1.0. Later, we will learn how normalizing values makes it easier to map values between variables.
5. Another important C feature we can see in this example is the presence of preprocessor macros. Macros are part of a pre-compilation step. With them it is possible to #define global variables and do some basic conditional operation (with #ifdef and #endif). All the macro commands begin with a hashtag (#). Pre-compilation happens right before compiling and copies all the calls to #defines and check #ifdef (is defined) and #ifndef (is not defined) conditionals. In our "hello world!" example above, we only insert the line 2 if GL_ES is defined, which mostly happens when the code is compiled on mobile devices and browsers.
6. Float types are vital in shaders, so the level of precision is crucial. Lower precision means faster rendering, but at the cost of quality. You can be picky and specify the precision of each variable that uses floating point. In the second line (precision mediump float;) we are setting all floats to medium precision. But we can choose to set them to low (precision lowp float;) or high (precision highp float;).
7. The last, and maybe most important, detail is that GLSL specs don’t guarantee that variables will be automatically casted. What does that mean? Manufacturers have different approaches to accelerate graphics card processes but they are forced to guarantee minimum specs. Automatic casting is not one of them. In our “hello world!” example vec4 has floating point precision and for that it expects to be assigned with floats. If you want to make good consistent code and not spend hours debugging white screens, get used to putting the point (.) in your floats. This kind of code will not always work:
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}Now that we've described the most relevant elements of our "hello world!" program, it's time to click on the code block and start challenging all that we've learned. You will note that on errors, the program will fail to compile, showing a white screen. There are some interesting things to try, for example:
* Try replacing the floats with integers, your graphic card may or may not tolerate this behavior.
* Try commenting out line 8 and not assigning any pixel value to the function.
* Try making a separate function that returns a specific color and use it inside main(). As a hint, here is the code for a function that returns a red color:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* There are multiple ways of constructing vec4 types, try to discover other ways. The following is one of them:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Although this example isn't very exciting, it is the most basic example - we are changing all the pixels inside the canvas to the same exact color. In the following chapter we will see how to change the pixel colors by using two types of input: space (the place of the pixel on the screen) and time (the number of seconds since the page was loaded).
---
02/Notes (02/notes.md)
Scott
* After reading 1–7 I understand that there is some relationship between shader language and C, but I’m not sure what it is. Can you tell us explicitly? Is GLSL (shader language) C? Is GLSL a subset of C?
* In point #5 you explain preprocessor macros, but you don’t explain why you used one here. You probably don’t want to go off topic just yet, but I would appreciate a sidebar or brief comment explaining why you are including special handling for GL_ES. (My mind goes: Interesting, but why is mobile OpenGL different from non-mobile?)
* Point #6: I hope later in the book you will show us an example of how changing float precision affects things visually. (I sense it’s important, but don’t see how yet, and I recognize it is too soon to address it here.)
* Point #7: I had to read this two times to get that you meant including the period (.) is casting. I haven’t used C, so I didn’t understand that. I am used to Processing/Java-style casting, like:
(float) 1 == 1.0* I figured out that vec4() is a vector with 4 values, but perhaps you could make that explicit somewhere.
---
02/README Ch (02/README-ch.md)
Hello World
“Hello world!”通常都是学习一个新语言的第一个例子。这是一个非常简单,只有一行的程序。它既是一个热情的欢迎,也传达了编程所能带来的可能性。
然而在 GPU 的世界里,第一步就渲染一行文字太难了,所以我们改为选择一个鲜艳的欢迎色,来吧躁起来!
<div class="codeAndCanvas" data="hello_world.frag"></div>
如果你是在线阅读这本书的话,上面的代码都是可以交互的。你可以点击或者改动代码中任何一部分,尽情探索。多亏 GPU 的架构,shader 会飞速地编译和更新,这使得你的改动都会立刻出现在你眼前。试试改动第 8 行的值,看会发生什么。
尽管这几行简单的代码看起来不像有很多内容,我们还是可以据此推测出一些知识点:
1. shader 语言 有一个 ``main` 函数,会在最后返回颜色值。这点和 C 语言很像。
2. 最终的像素颜色取决于预设的全局变量 `gl_FragColor`。
3. 这个类 C 语言有内建的变量(像`gl_FragColor`),函数和数据类型。在本例中我们刚刚介绍了`vec4`(四分量浮点向量)。之后我们会见到更多的类型,像 `vec3` (三分量浮点向量)和 `vec2` (二分量浮点向量),还有非常著名的:`float`(单精度浮点型), `int`(整型) 和 `bool`(布尔型)。
4. 如果我们仔细观察 `vec4` 类型,可以推测这四个变元分别响应红,绿,蓝和透明度通道。同时我们也可以看到这些变量是规范化的,意思是它们的值是从0到1的。之后我们会学习如何规范化变量,使得在变量间map(映射)数值更加容易。
5. 另一个可以从本例看出来的很重要的类 C 语言特征是,预处理程序的宏指令。宏指令是预编译的一部分。有了宏才可以 `#define` (定义)全局变量和进行一些基础的条件运算(通过使用 `#ifdef` 和 `#endif`)。所有的宏都以 `#` 开头。预编译会在编译前一刻发生,把所有的命令复制到 `#defines` 里,检查`#ifdef` 条件句是否已被定义, `#ifndef` 条件句是否没有被定义。在我们刚刚的“hello world!”的例子中,如果定义了`GL_ES`这个变量,才会插入运行第2行的代码,这个通常用在移动端或浏览器的编译中。
6. `float`类型在 shaders 中非常重要,所以精度非常重要。更低的精度会有更快的渲染速度,但是会以质量为代价。你可以选择每一个浮点值的精度。在第一行(`precision mediump float;`)我们就是设定了所有的浮点值都是中等精度。但我们也可以选择把这个值设为“低”(`precision lowp float;`)或者“高”(`precision highp float;`)。
7. 最后可能也是最重要的细节是,GLSL 语言规范并不保证变量会被自动转换类别。这句话是什么意思呢?显卡的硬件制造商各有不同的显卡加速方式,但是却被要求有最精简的语言规范。因而,自动强制类型转换并没有包括在其中。在我们的“hello world!”例子中,`vec4` 精确到单精度浮点,所以应被赋予 `float` 格式。但是如果你想要代码前后一致,不要之后花费大量时间 debug 的话,最好养成在 `float` 型数值里加一个 `.` 的好习惯。如下这种代码就可能不能正常运行:
void main() {
gl_FragColor = vec4(1,0,0,1); // 出错
}现在我们已经基本讨论完了“hello world!”例子中所有主要的内容,是时候点击代码,检验一下我们所学的知识了。你会发现出错时程序会编译失败,只留一个寂寞的白屏。你可以试试一些好玩的小点子,比如说:
* 把单精度浮点值换成整型数值,猜猜你的显卡能不能容忍这个行为。
* 试试把第八行注释掉,不给函数赋任何像素的值。
* 尝试另外写个函数,返回某个颜色,然后在 `main()` 里面使用这个函数。给个提示,这个函数应该长这样:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* 有很多种构造 `vec4` 类型的方式,试试看其他方式。下面就是其中一种方式:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);尽管这个例子看起来不那么刺激,它却是最最基础的 —— 我们把画布上的每一个像素都改成了一个确切的颜色。在接下来的章节中我们将会看到如何用两种输入源来改变像素的颜色:空间(依据像素在屏幕上的位置)和时间(依据页面加载了多少秒)。
---
02/README De (02/README-de.md)
Hallo Welt
Beim Erlernen einer neuen Programmiersprache beginnt man häufig mit dem berühmten „Hello world!“-Beispiel. Dabei handelt es sich um einen simplen Einzeiler, der die Meldung „Hello world!“ auf den Bildschirm bringt - als Gruß an die Welt und als Ausdruck der Vorfreude auf die kommenden Möglichkeiten.
In der Welt der Shader-Programmierung ist die Textausgabe eine zu komplizierte Angelegenheit, um gleich damit zu beginnen. Stattdessen wollen wir eine leuchtende Farbe als Willkommensgruß auf den Bildschirm zaubern.
<div class="codeAndCanvas" data="hello_world.frag"></div>
Falls Du dieses Buch in einem Internet-Browser liest, ist der obige Programmcode interaktiv. Du kannst in das Listing hineinklicken und jeden Teil des Programmcodes ändern. Deine Änderungen werden sofort innerhalb der Zeichenfläche sichtbar, weil der Shader-Code automatisch kompiliert und ausgeführt wird. Versuche es doch einfach einmal, indem Du die Zahlenwerte in der Programmzeile 8 änderst.
Obwohl diese wenigen, einfachen Programmzeilen noch nicht nach viel aussehen, können wir daraus bereits einige Erkenntnisse gewinnen:
1. Shader-Programme verfügen über genau eine `main`-Funktion, die an ihrem Ende einen Farbcode zurückliefert. Das erinnert stark an die Programmiersprache C, nur dass es dort nicht immer um Farbcodes geht.
2. Der berechnete Farbwert wird für die Rückgabe in die reservierte globale Variable `gl_FragColor` eingetragen.
3. Die stark von C beeinflusste Programmiersprache für Shader verfügt über eingebaute Variablen (so wie `gl_FragColor`), Funktionen und Datentypen. Im obigen Beispiel sehen wir bereits den Datentyp `vec4`, der einen vierdimensionalen Vektor aus Fließkommazahlen repräsentiert. Im weiteren Verlauf des Buches werden wir noch die Typen `vec3` und `vec2` kennen lernen, ebenso die wichtigen Typen `float`, `int` und `bool`.
4. Wenn wir uns den `vec4` Datentyp im obigen Beispiel genau anschauen, können wir bereits erahnen, dass die vier Zahlenwerte für die Farbkanäle Rot, Grün, Blau und Alpha (ein Maß für die Deckkraft) stehen. Außerdem erkennen wir, dass diese Werte offensichtlich normalisiert sind, sich also zwischen `0.0` und `1.0` bewegen. Später werden wir noch sehen, dass es uns diese Normalisierung vereinfacht, die Inhalte von Variablen auf Farbwerte abzubilden.
5. Eine weiteres aus C bekanntes Element sind die Präprozessor-Makros, die auch in unserem obigen Beispiel auftauchen. Mit ihrer Hilfe lassen sich Konstanten definieren (`#define`) und konditionale Festlegungen treffen (mit `#ifdef` und `#endif`). Alle diese Makrobefehle beginnen mit einer Raute (`#`). Ihre Auswertung erfolgt als erster Schritt noch vor der eigentlichen Kompilierung des Shaders. Das Ergebnis dieser Auswertung bestimmt jeweils, welche Zeilen und Ausdrücke tatsächlich in den Programmcode einfließen, der anschließend kompiliert wird. In unserem obigen Beispiel wird die Programmzeile 2 beispielsweise nur dann übernommen, wenn das Symbol `GL_ES` definiert ist. Dies ist in der Regel nur in Umgebungen auf mobilen Geräten der Fall, d.h. wenn das obige Programm beispielsweise auf einem Smartphone kompiliert wird, ist die Zeile 2 darin vorhanden und wirkt sich entsprechend aus. Beim Kompilieren auf einem Laptop oder PC taucht die Zeile 2 aber gar nicht auf, weil dort auch das Symbol `GL_ES` nicht definiert ist.
6. Fließkomma-Datentypen sind ganz entscheidend für die Berechnungen innerhalb von Shadern. Deshalb spielt die Genauigkeit dieser Operationen eine wichtige Rolle. Eine geringere Genauigkeit bedeutet weniger Aufwand und geht deshalb mit einer schnelleren Berechnung einher. Der Preis dafür sind allerdings Einbußen bei der Qualität der Berechnungen. Wenn man will, kann man die Genauigkeit jeder einzelnen Fließkommavariablen genau festlegen. Meist unterscheidet man jedoch grundsätzlich, je nach den Möglichkeiten der Plattform, auf der ein Shader zur Ausführung kommen soll. So setzen wir hier in der zweiten Programmzeile die Genauigkeit auf Medium (`precision mediump float;`), wenn der Shader auf einem Mobilgerät kompiliert wird. Darüber hinaus gibt es noch die Genauigkeitsstufen „niedrig“ (`precision lowp float;`) und „hoch“ (`precision highp float;`). Letzteres ist auf vielen Plattformen die Vorgabe, sofern wir nichts Anderes festlegen.
7. Die letzte und für die Praxis vielleicht wichtigste Erkenntnis aus dem obigen Programmcode ist, dass wir Fließkommawerte immer mit einem Punkt („.“) versehen sollten, auch wenn der Nachkommateil leer bleibt (Null ist). Denn sonst macht der Compiler möglicherweise Integer-Werte (Ganzzahlen) daraus, und das kann während der Ausführung des Shaders zu hässlichen Programmfehlern führen. Die Zeichenfläche bleibt dann einfach weiß, weil es nicht weitergeht. Wenn du guten und auf vielen Shader-Plattformen ausführbaren Code entwickeln möchtest, gewöhne Dir die Kennzeichnung als Fließkommazahl durch den Punkt am besten gleich an. Denn der folgende Programmcode wird beispielsweise auf einigen Shader-Plattformen funktionieren, auf anderen hingegen nicht:
void main() {
gl_FragColor = vec4(1,0,0,1); // potenzielle Fehlerquelle!
}So, wo wir nun die wichtigen Elemente unseres kleinen „Hello world!“-Programms beschrieben haben, ist es an der Zeit, mit Veränderungen am Programmcode zu experimentieren. Du wirst feststellen, dass sich das Programm bei Syntaxfehlern oder unbekannten Befehlen weigert, zu kompilieren, und die Zeichenfläche deshalb weiß bleibt. Es gibt einige interessante Dinge, die Du direkt einmal ausprobieren kannst, beispielsweise:
* Versuche, die Fließkommazahlen durch Ganzzahlen zu ersetzen. Dann wirst Du unmittelbar feststellen, ob Deine aktuelle Umgebung/Grafikkarte damit umgehen kann.
Versuche, die Programmzeile 8* auszukommentieren, und schau was passiert, wenn der Code dadurch keinen Farbwert zurückliefert.
* Versuche, eine zusätzliche Funktion in den Programmcode einzufügen, die eine von Dir gewählte Farbe zurückliefert und rufe diese Funktion innerhalb von `main()` auf. Dazu ein Tipp: Hier folgt der Programmcode für eine Funktion, die Rot zurückliefert:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* Es gibt verschiedene Wege, um einen Wert vom Typ `vec4` zu konstruieren. Versuche, weitere Wege zu entdecken. So geht es zum Beispiel auch:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Obwohl dieses „Hello world!“-Programm noch nicht so wahnsinnig aufregend daherkommt, ist es doch das simpelste Beispiel aus der Welt der Shader. Wir beeinflussen damit die Farbe aller Bildpunkte innerhalb unserer Zeichenfläche. In den nun folgenden Kapiteln werden wir die Farbe der einzelnen Bildpunkte aufgrund von zwei unterschiedlichen Arten von Eingabewerten steuern: Ihrer Position (d.h. die Lage des zu bearbeitenden Pixels innerhalb der Zeichenfläche) und der Zeit (d.h. der Zeitspanne, die seit dem Laden einer Seite vergangen ist).
---
02/README Es (02/README-es.md)
Hola mundo
Usualmente el "Hola mundo" es el primer ejemplo con el que se aprende un lenguaje de programación. Se trata de una simple linea de código que imprime un entusiasta mensaje de bienvenida.
En el mundo de las GPU renderizar texto es una tarea complicada para ser el primer paso, por lo tanto nosotros vamos a imprimir un brillante color de bienvenida para mostrar nuestro entusiasmo.
<div class="codeAndCanvas" data="hello_world.frag"></div>
Si estás leyendo este libro en un navegador, el código anterior es interactivo, eso significa que puedes hacer clic y cambiar cualquier línea de código para explorar cómo funciona. Los cambios se reflejaran automáticamente gracias a que la arquitectura de la GPU se encarga de compilar y reemplazar los shaders al instante. Intenta modificar el contenido de la línea 6.
Aunque estas simples líneas de código no parezcan mucho, podemos inferir mucha información importante de ellas:
1. Los shaders tienen una función `main` principal que devuelven un color al final. Esto es muy similar a C.
2. El color final del pixel es guardado en la variable global reservada `gl_FragColor`.
3. Este lenguaje similar a C tiene variables reservadas (como `gl_FragColor`), funciones y tipos de variables. En este caso vemos que existe `vec4` que es un tipo de variable de 4 dimensiones de punto flotante. Más adelante veremos otros tipos de variables como `vec3` y `vec2` junto con las populares: `float`, `int` y `bool`.
4. Si miramos detenidamente el `vec4` podemos inferir que los cuatro argumentos pasados son el canal RED (rojo), el canal GREEN (verde), el canal BLUE (azul) y el canal ALPHA (transparencia). Además podemos ver que los valores se encuentran normalizados, eso significa que van desde `0.0` a `1.0`. Más adelante aprenderemos que normalizar valores vuelve mucho más fácil nuestro trabajo con las variables.
5. Otra función de C que vemos en el ejemplo son los macros al preprocesador. Los macros son parte del proceso de precompilado. Con ellos es posible definir variables globales (con `#define`) y hacer operaciones condicionales básicas ( con `#ifdef` y `#endif`). Todos los comandos macro comienzan con un numeral (`#`). La pre-compilación sucede en el momento previo a la compilación y chequea todos los `#defines`, y los condicionales `#ifdef` (está definido) y `#ifndef` (no está definido). En nuestro ejemplo, el "Hola mundo", solamente insertamos la segunda línea de código si `GL_ES` está definida, que la mayoría de las veces se encuentra definida cuando el código es compilado en mobile o en navegadores.
6. Los valores flotantes son vitales en los shaders, ya que el nivel de precisión es crucial. A menor precisión mayor velocidad de render, pero peor calidad. Podemos ser meticulosos y especificar la precisión de cada variable que use punto flotante. En la primera línea (`precision mediump float;`) estamos ajustando todos los valores flotantes a una precisión media. Pero podríamos configurarlos en low (`precision lowp float;`) o high (`precision highp float;`).
7. El último detalle, y quizá el más importante, es que las especificaciones de GLSL no garantizan que las variables sean automáticamente convertidas. ¿Qué significa eso? Los manufacturadores de GPU tienen diferentes estrategias para acelerar los gráficos pero están forzados a entregar especificaciones mínimas, por lo que la conversión automática de variables no es algo importante. Si queremos que nuestro código sea consistente y no pasar horas depurando pantallas blancas, tenemos que acostumbrarnos a usar el punto ( `.` ) en los flotantes. Este código no siempre funcionará:
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}Ahora que ya describimos los elementos más importantes de nuestro "Hola mundo", es hora de hacer clic en el código y poner en práctica nuestros conocimientos aprendidos. Notarás que cuando hay errores, el programa no compilará, y mostrará una pantalla blanca. Aqui hay algunas cosas interesantes que puedes probar, por ejemplo:
* Intenta modificar los flotantes y poner enteros, es posible que tu placa de video no tolere esto.
* Prueba comentar la línea 6 y no asignar ningún valor a la función.
* Intenta crear una función separada que devuelva un color específico y usalo dentro del `main()`. Una pista, aqui está el código que usaríamos para devolver el color rojo:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* Hay muchas formas de construir un `vec4`, intenta descubrir nuevas formas de hacerlo. La siguiente es un ejemplo:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Mas alla de que el ejemplo no sea muy emocionante, es el ejemplo más básico que podemos crear - estamos cambiando todos los pixeles de la pantalla al mismo tiempo y asignándole a todos el mismo color. En el siguiente capítulo veremos cómo cambiar los colores de los pixeles utilizando dos tipos de entradas: espacio (la posición del pixel en la pantalla) y tiempo (el número de segundos desde que la página fue cargada).
---
02/README Fa (02/README-fa.md)
Hello World
معمولا عبارت "Hello World" مثالی از اولین قدم برای یادگیری یک زبان جدید است.
در مورد ما ارائه یک متن کار پیچیده ای برای قدم اول است، در عوض یک رنگ روشن را به عنوان شور و شوق اولین کدمان اجرا میکنیم!
<div class="codeAndCanvas" data="hello_world.frag"></div>
اگر این کتاب را در یک مرورگر مدرن میخوانید، کد بالا قابل تغییر است، تغییرات شما بلافاصله کامپایل و به شما نمایش داده میشوند، سعی کنید خط 8 را تغییر دهید.
البته این خطوط ساده به نظر نمیرسند، اما میتوان مفاهیم قابل توجهی از آنها استباط کرد:
1. برنامه نویسی شیدر یک عملکرد اصلی 'main' دارد، که در انتها یک رنگ برمیگرداند. شبیه زبان C.
2. رنگ نهایی پیکسل به متغیر گلوبال gl_FragColor اختصاص داده شده است.
3. این زبان شبه C با متغیر ها و تابع های مختلف ساخته شده و استفاده میشود. در این مورد ما با vect4 آشنا شدیم که مخفف یک بردار 4 بعدی با دقت شناور(float) است. انواع بیشتری ازین متغیر ها مانند vect2, vect3 و bool, int, float در آینده خواهیم دید.
4. اگر به vect4 دقت کنیم مییابیم این 4 آرگومان به کانال های RGBA پیکسل مورد نظر پاسخ میدهند. همچنین این مقادیر نرمال شده اند یعنی بین 0تا1(نه بین 0 تا 255)، بعدا خواهیم آموخت چگونه نرمال سازی مقادیر، ترسیم مقادیر بین متغیر ها را آسان میکند.
5. یکی از دیگر ویژگی های مشابه C مثال بالا وجود ماکرو ها هستند. با استفاده از آنان میتوان متغیر های جهانی را تعریف کرد و یا برخی عملیات شرطی اساسی را انجام داد(ifdef and #endif#). تمام دستورات کلان(ماکرو) با هشتگ شروع میشوند. ماکرو ها قبل از کامپایل اجرا میشوند، شرایط را بررسی میکند(ifdef and #endif#) و ارجاعات به defines# را کپی میکند. مثلا در مثال بالا ما خط 2 را در صورت تعریف GL_ES وارد میکنیم. که معمولا در هنگام کامپایل این کد در تلفن های همراه و مرورگر ها اتفاق میافتد.
6. شناور ها(float) در شیدر ها حیاتی هستند، بنابراین سطح دقت بسیار مهم است. دقت پایین تر به معنای رندر سریع تر است، همچنین کیفیت کمتر. میتوانید مثل من خط دو مثال بالا متغیر های شناور را با دقت متوسط درنظر بگیرید(precision mediump float)، همچنین میتوانید متغیر شناور با دقت پایین(precision lowp float) یا بالا(precision highp float)هم میتوان در نظر گرفت.
7. آخرین و شاید مهمترین نکته در مثال بالا اینکه در GLSL تضمین نمیشود که متغیر ها به طور خودکار تغییر نوع داده(casting) رویشان اعمال شود. این به چه معناست؟ تولید کنندگان رویکرد های مختلفی برای سرعت بخشیدن روند پردازش کارت های گرافیکی دارند، و مجبورند حداقل مشخصات را تضمین کنند. کستینگ ازین رویکرد ها نیست. در مثال بالا vect4 دارای نقطه شناور است و برای آن انتظار میرود که به متغیر های شناور انتساب شود. اگر میخواهید کد سازگار خوبی ایجاد کنید و ساعت ها وقت صرف دیبگ کردن آن نکنید، عادت کنید در float ها از نقطه(.) استفاده کنید.
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}اکنون که مهم ترین عناصر مثال بالا را با هم مرور کردیم، وقت آن است که روی کد کلیک کنید و چیز هایی که یاد گرفتیم را پیاده کنیم. توجه کنید که در صورت خطا برنامه صفحه سفید را به شما نشان میدهد. بعضی چیز های جالب وجود دارد که باید امتحان کنید، مثلا:
* شناور ها را با اعداد صحیح(int) جایگزین کنید، کارت گرافیک شما معلوم نیست بتواند این کد را اجرا کند یا خیر.
* سعی کنید خط 8 را کامنت کنید و هیچ مقدار پیکسلی به تابع اختصاص ندهید.
* سعی کنید یک تابع جداگانه ایجاد کنید که رنگ خاصی را برگرداند و از آن داخل main استفاده کنید، به عنوان یک راهنمایی در اینجا کد مربوط به تابعیست که رنگ قرمز را بر میگرداند.
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* روش ها مختلفی برای ساخت vect4 وجود دارد، سعی کنید راه های دیگر را پیدا کنید، یکی از آن ها به این صورت است:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);اگرچه این مثال خیلی هیجان انگیز نیست، اما ابتدایی ترین مثال است. ما تمام پیکسل های داخل کنوس(canvas) را به یک رنگ تغییر دادیم. در فصل بعد نحوه تغییر رنگ پیکسل را با استفاده از دو نوع ورودی دیگر میبینیم. فضا(محل و مختصات پیکسل روی صفحه) و زمان(مقدار زمان از لحظه بارگیری صفحه بر حسب ثانیه).
---
02/README Fr (02/README-fr.md)
Hello World
Généralement le "Hello World!" est la première étape dans l'apprentissage d'un nouveau langage. Il s'agit d'un programme court qui renvoie un message enthousiaste et fait miroiter un avenir radieux.
Au pays du GPU, afficher du texte est une tâche extrêmement complexe pour une première étape, à la place nous allons choisir une couleur chatoyante pour déchaîner notre enthousiasme !
<div class="codeAndCanvas" data="hello_world.frag"></div>
Si vous lisez ceci dans un navigateur récent, le bloc précédent est interactif. Cela signifie que vous pouvez cliquer et éditer le code.Les modifications seront mises à jour immédiatement grâce à l'architecture GPU qui compile et remplace les shaders à la volée. Vous pouvez essayer de changer les valeurs de la ligne 8.
Si ce programme paraît simple, nous pouvons déjà faire quelques observations intéressantes :
1. Ça ressemble à du C, un shader possède une fonction main dont le seul but est d'assigner une couleur.
2. La couleur du pixel est assignée à une variable globale réservée gl_FragColor.
3. Ce langage aux allures de C, possède des variables (comme gl_FragColor), des fonctions et des types. Dans cet exemple, nous venons de voir le type vec4 qui représente un vecteur de chiffres à virgule flottante (float) à 4 dimensions. Plus tard nous verrons d'autres types comme vec3 & vec2 avec les plus communs comme : float, int & bool.
4. Si l'on regarde vec4 de plus près, on peut déduire que les 4 arguments correspondent aux 4 canaux: RED, GREEN, BLUE & ALPHA du pixel. On note également que les valeurs sont normalisées, ce qui signifie qu'elles vont de 0.0 à 1.0. Plus tard, nous verrons comment la normalisation des valeurs facilite la mise en correspondance (map) des valeurs avec des variables.
5. Une autre caractéristique du C importante que nous pouvons voir dans cet exemple est la présence de macros de préprocesseur. Les macros font partie de l'étape de pré-compilation, elles permettent de définir (#define) des variables globales et de faire des opérations conditionnelles de base (avec #ifdef & #endif). Toutes les macros commencent par un hashtag (#). La pré-compilation se produit - comme son nom l'indique - avant la compilation du shader, elle copie et renseigne tous les #define et vérifie les inclusions conditionnelles #ifdef (si défini) et #ifndef (si non défini). Dans l'exemple ci-dessus, nous n'insérons la ligne 2 que si GL_ES est défini, ce qui se produit principalement lorsque le code est compilé sur mobile ou dans un navigateur.
6. Le type float est vital dans les shaders, donc le niveau de précision des nombres flottants est crucial. Une précision basse permet un rendu plus rapide mais une qualité moindre et inversement, une précision élevée permet un meilleur rendu au prix de performances réduites. On peut spécifier la précision de chaque variable se servant de, ligne 2, precision mediump float;, nous assignons la précision medium à tous les flottants de l'application. Nous pourrions leur donner une valeur basse (precision lowp float;) ou haute (precision highp float;).
7. La dernière remarque, probablement la plus importante, est que les spécifications GLSL ne garantissent pas que les variables seront castées automatiquement. Qu'est-ce que cela veut dire ? Les fabricants ont des approches différentes pour accélérer leurs cartes graphiques mais ils doivent toutefois se conformer au minimum à un ensemble de spécifications communes. Le casting automatique des variables ne fait pas partie des spécifications. Dans notre exemple, vec4 attend des floats et rien d'autre. Si vous voulez obtenir un code homogène et éviter de passer des heures à débugger un écran blanc, prenez l'habitude de mettre des points (.) dans vos floats. Ce genre de code ne marchera pas toujours :
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}Alors que celui ci à de plus grandes chances de marcher :
void main() {
gl_FragColor = vec4(1., .0, 0., 1.); // BETTER
}Notez que le point peut être placé avant ou après un 0.
Maintentant que nous avons passé en revue quelques éléments importants du programme "Hello World!", il est temps de cliquer sur le bloc de code et de mettre en application ce que nous venons d'apprendre.
Vous remarquerez que le programme ne se recompilera pas tant qu'il restera des erreurs. Voici quelques idées intéressantes à tester :
Essayez de remplacer les float par des int* la carte graphique vous autorisera ou non à le faire.
* Essayez de commenter (//) la ligne 8, ce qui n'assignera aucune valeur au pixel.
* Essayez de créer une fonction qui retourne une couleur en dehors de main() et essayez de l'utiliser pour assigner la valeur de gl_FragColor dans main(). Pour vous aider, voici le code d'une fonction qui retourne une couleur rouge :
vec4 red() {
return vec4(1.0,0.0,0.0,1.0);
}* On peut construire un vec4 de plusieurs façons, essayez de découvrir d'autres manières. Par exemple:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Bien que cet exemple ne soit pas très excitant, c'est le plus basique qui soit - nous donnons à l'ensemble des pixels de l'image la même couleur. Dans le chapitre suivant, nous verrons comment changer cette couleur en fonction de 2 facteurs : l'espace (l'emplacement du pixel à l'écran) et le temps (le nombre de secondes écoulées depuis le chargement de la page).
---
02/README Id (02/README-id.md)
Hello World
Biasanya contoh "Hello World" adalah langkah pertama untuk mempelajari bahasa baru. Ini sebuah program satu baris sederhana yang menampilkan pesan sambutan yang antusias dan menyatakan peluang di depan.
Dalam rendering teks, GPU adalah tugas yang terlalu rumit untuk langkah pertama, sebagai gantinya kami akan memilih warna sambutan yang cerah untuk meneriakkan antusiasme kami!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Jika anda membaca buku ini dalam browser, blok kode sebelumnya itu interaktif. Itu maksudnya anda dapat mengklik dan mengganti bagain manapun dari kode itu yang ingin anda jelajahi. Perubahan akan segera diperbarui berkat arsitektur GPU yang mengompilasi dan menggantikan shader dengan cepat . Cobalah dengan mengubah nilai pada baris 8.
Meskipun kode sederhana ini tidak terlihat banyak, kita kita dapat menyimpulkan pengetahuan substansial darinya:
1. Bahasa Shader memiliki satu fungsi main yang mengembalikan warna pada akhir. Ini sama seperti C.
2. Warna piksel terakhir disimpan ke variabel global yang disediakan gl_FragColor.
3. Bahasa beraneka C ini telah membuat variabel (seperti gl_FragColor), fungsi dan tipe. Dalam hal ini kita baru saja diperkenalkan dengan vec4 yang merupakan singkatan dari vektor empat dimensi presisi floating point. Nanti kita akan melihat lebih banyak tipe seperti vec3 dan vec2 bersama dengan yang populer: float,int dan bool.
4. Jika kita melihat lebih dekat ke tipe vec4 kita dapat menyimpulkan bahwa empat argumen menanggapi saluran MERAH, HIJAU, BIRU dan ALPHA. Kita juga dapat melihat bahwa nilai-nilai ini dinormalisasi, yang berarti nilainya berubah dari 0.0 ke 1.0. Nanti, kita akan belajar bagaimana menormalkan nilai membuatnya lebih mudah untuk memetakan nilai antar variabel.
5. Fitur C penting lainnya yang dapat kita lihat dalam contoh ini adalah keberadaan makro praprosesor. Makro adalah bagian dari langkah pra-kompilasi. Dengan mereka dimungkinkan untuk #define variabel global dan melakukan beberapa operasi bersyarat dasar (dengan #ifdef dan #endif). Semua perintah makro dimulai dengan hashtag (#). Pra-kompilasi terjadi tepat sebelum mengkompilasi dan menyalin semua panggilan ke #defines dan centang #ifdef (ditentukan) dan kondisional #ifndef (tidak ditentukan). Dalam "Hello World!" Contoh di atas, kita hanya menyisipkan baris 2 jika GL_ES ditentukan, yang sebagian besar terjadi ketika kode dikompilasi pada perangkat seluler dan browser.
6. Tipe float sangatlah penting dalam shader, jadi tingkat presisi sangat tinggi. Presisi yang lebih rendah artinya semakin cepat waktu renderingnya, tapi dengan biaya kualitas. Anda dapat memilih-milih dan menentukan presisi setiap variabel menggunakan floating point. Dalam baris kedua ('precision mediump float;') kita menyetel seluruh float ke presisi medium. Tetapi kita dapat memilih untuk menyetel mereka ke presisi rendah (precision lowp float) atau tinggi (precision highp float;).
7. Yang terakhir, dan mungkin yang terpenting, detailnya bahwa spesifikasi GLSL tidak menjamin bahwa variabel akan otomatis ditransmisikan. Apa maksudnya? Pabrikan memiliki pendekatan berbeda untuk mengakselerasikan proses kartu grafis tapi merka dipaksa untuk menjamin spesifikasi minimum. Transmisi otomatis bukan salah satunya. Dalam "Hello World!" contoh vec4 memiliki ketepatan titik mengambang dan untuk itu diharapkan akan ditetapkan dengan floats. Jika Anda ingin membuat kode yang konsisten dan tidak menghabiskan waktu berjam-jam untuk men-debug layar putih, biasakan untuk meletakkan titik (.) di float Anda. Kode semacam ini tidak akan selalu berfungsi:
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}Sekarang kita telah menjelaskan elemen paling relevan dari "halo dunia!" program, saatnya untuk mengklik blok kode dan mulai menantang semua yang telah kita pelajari. Anda akan melihat bahwa pada kesalahan, program akan gagal untuk dikompilasi, menampilkan layar putih. Ada beberapa hal yang menarik untuk dicoba, misalnya:
* Mencoba mengganti floats dengan integers, kartu grafis anda mungkin akan atau mungkin tidak akan mentolenransi perilaku ini.
* Mencoba untuk mengkomentari baris 8 dan tidak memberi nilai piksel apa pun pada fungsi.
* Mencoba membuat fungsi terpisah yang mengembalikan spesisif warna dan menggunakannya dalam main(). Sebagai petunjuk, ini adalah kode yang mengembalikan warna merah:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* Ada banyak cara untuk membangun tipe vec4, coba untuk menemukan jalan lain. Berikut ini adalah salah satu contohnya:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Meskipun contoh ini tidak terlalu seru, ini adalah contoh dasar - kita mengubah seluruh piksel dalam kanvas menjadi warna tertentu yang sama. Dalam bab berikutnya kita akan melihat bagaimana cara mengubah warna piksel dengan menggunakan dua tipe input: ruang (tempat piksel pada layar) dan waktu (jumlah detik semenjak halaman dimuat).
---
02/README It (02/README-it.md)
Ciao Mondo
Di solito l'esempio "Ciao Mondo!" è il primo passo per imparare un nuovo linguaggio. Si tratta di un semplice programma di una riga che visualizza un messaggio entusiastico di benvenuto.
Nel mondo GPU rendere un testo è un esercizio troppo complicato per un primo passo, perciò sceglieremo un colore brillante di benvenuto per mostrare il nostro entusiasmo!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Se stai leggendo questo libro in un navigatore, noterai che il precedente blocco di codice è interattivo. Ciò significa che è possibile fare click e modificare qualsiasi parte del codice per capire come funziona. Le modifiche verranno aggiornate immediatamente grazie all'architettura GPU che compila e sostituisce gli shaders al volo. Prova per esempio a cambiare i valori della linea 6.
Anche se queste semplici righe di codice non sembrano essere molto importanti, possiamo trarre molte informazioni:
1. Gli Shaders hanno una unica funzione `main` che alla fine restituisce un colore. Questa caratteristica è molto simile al linguaggio C.
2. Il colore finale dei pixel viene assegnato a una variabile globale riservata `gl_FragColor`.
3. Questo linguaggio è simile al C e ha variabili built-in (come `gl_FragColor`), funzioni e tipi. In questo esempio abbiamo introdotto `vec4` che è un vettore a virgola mobile(float) di quattro dimensioni. Più avanti incontreremo altri tipi come `vec3` e `vec2` insieme ai più popolari: `float`, `int` e `bool`.
4. Se analizziamo il tipo `vec4` possiamo dedurre che i quattro argomenti corrispondono ai canali ROSSO, VERDE, BLU e ALFA. Inoltre possiamo notare che questi valori sono normalizzati, il che significa che vanno da `0.0` a `1.0`. In seguito, impareremo che normalizzare i valori rendere più facile mapparli fra le variabili.
5. Un'altra importante caratteristica del C che si può vedere in questo esempio è la presenza di macro del preprocessore. Le macro sono parte di una fase di pre-compilazione. Con loro è possibile `#define` variabili globali e fare alcune funzioni condizionali di base (con `#ifdef` e `#endif`). Tutti i comandi macro iniziano con un hashtag (`#`). La pre-compilazione avviene appena prima di compilare lo shader e copia tutte le chiamate a `#defines` e verifica `#ifdef` (è definito) e `#ifndef` (non è definito) nel caso di chiamate condizionali . Nel nostro esempio precedente "Ciao mondo!", abbiamo inserito solo la linea 2 per verificare se `GL_ES` è definito, chiamata che avviene soprattutto quando il codice viene compilato su dispositivi mobili e browser.
6. I tipi float sono vitali negli shader, dove il livello di precisione è fondamentale. Precisione inferiore significa resa più veloce, ma a scapito della qualità. Si può essere pignoli e specificare la precisione di ogni variabile che utilizza la virgola mobile. Nella prima riga (`precision mediump float;`) stiamo definendo tutti i float a media precisione. Ma possiamo impostare la loro precisione a un livello basso (`precision lowp float;`) o alto (`precision highp float;`).
7. L'ultimo, e forse più importante, dettaglio è che le specifiche GLSL non garantiscono che le variabili siano convertite automaticamente. Cosa significa? I produttori delle carte grafiche hanno differenti approcci per accelerare i processi delle carte grafiche, ma sono costretti a garantire delle specifiche minime. La conversione automatica non è una di queste. Nel nostro esempio "Ciao mondo", `vec4` ha precisione in virgola mobile e per questo ci si aspetta dei `floats`. Se si vuole realizzare un codice omogeneo e non passare ore a fare il debug di schermi bianchi, abituatevi a mettere il punto ( `.` ) all'interno dei vostri float. Questo tipo di codice non funziona sempre:
void main() {
gl_FragColor = vec4(1,0,0,1); // ERRORE
}Ora che abbiamo descritto gli elementi più rilevanti del nostro programma "ciao mondo!", è il momento di cliccare sul blocco di codice e iniziare a mettere alla prova tutto quello che abbiamo imparato. Noterete che in caso di errore, il programma non sarà in grado di compilare e mostrerà uno schermo bianco. Ci sono alcune cose interessanti da provare, per esempio:
* Provate a sostituire i float con degli integer, la vostra scheda grafica potrebbe o non accettare questo comportamento.
* Commentate la linea 6 e non assegnate alcun valore ai pixel della funzione.
* Provate a fare una funzione separata che restituisce un colore specifico e utilizzarla all'interno del `main()`. Piccolo suggerimento, ecco il codice per una funzione che restituisce un colore rosso:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* Ci sono diversi modi per costruire dei tipi `vec4`, prova a scoprire gli altri modi. Per esempio:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Anche se questo esempio non è molto eccitante, è il più elementare possibile - stiamo cambiando tutti i pixel all'interno del canvas con il medesimo colore. Nel prossimo capitolo vedremo come cambiare i colori dei pixel utilizzando due tipi di input: spazio (cioè la posizione dei pixel sullo schermo) e il tempo (cioè il numero di secondi da quando la pagina è stata caricata).
---
02/README Jp (02/README-jp.md)
ハロー・ワールド!
新しいプログラミング言語を学ぶときはたいてい最初に「Hello World」を表示します。一行のシンプルなプログラムで新しい世界に高らかにご挨拶というわけです。
しかしGPUの世界で文字を書くのは第一歩にしてはかなりの大仕事です。代わりに鮮やかな色でこの熱意を表現することにしましょう。
<div class="codeAndCanvas" data="hello_world.frag"></div>
もしこの本をブラウザーで読んでいるのであれば、上のサンプルコードをクリックして好きなところを編集することができます。GPUは変更した内容を反映してコードをコンパイルし、シェーダーを即座に置き換えます。試しに6行目の値を書き換えてみましょう。
一見シンプルなコードですが、ここから多くのことが読み取れます。
1. C言語と同じように、シェーダー言語は結果として色を返す`main()`関数を持っています。
2. 最終的なピクセルの色は、予約語として確保されたグローバル変数、`gl_FragColor`に割り当てられます。
3. このCによく似た言語には(`gl_FragColor`のような)組み込みの「変数」や「関数」、「型」があります。このサンプルでは浮動小数点精度を持つ4次元ベクトル `vec4` 型が使われています。またこの後、`vec3`、`vec2` のような型や、おなじみの `float`、`int`、`bool` なども登場します。
4. `vec4`型をよく見ると、4つの引数はそれぞれ赤(Red)、緑(Green)、青(Blue)、透過度(Alpha)の各チャンネルに対応していることが分かるでしょう。またこれらの値が正規化されている(normalized)、つまり `0.0` から `1.0` の値をとることも読み取れます。後ほど、値が正規化されていると変数間の写像に便利であることを学びます。
5. プリプロセッサマクロが使えるのも、このサンプルから読み取れるもう一つの大事な「C言語的」特徴です。マクロはコンパイルの前に処理されます。マクロを使うとグローバル変数を定義(`#define`)したり、`#ifdef` と `#endif` を使って場合分けを行うことができます。全てのマクロの命令はハッシュタグ(`#`)で始まります。コンパイルの直前には全ての `#define` の呼び出しが置き換えられ、`#ifdef` や `#ifndef` といった条件文のチェックが行なわれます。上のサンプルでは、`GL_ES` が定義されているときにだけ2行目のコードを挿入します。`GL_ES` は多くの場合、モバイル機器やブラウザー上でコンパイルされたことを意味します。
6. 浮動小数点型はシェーダーに不可欠で、その精度はとても重要な意味を持ちます。精度が低いとレンダリングが速くなる代わりに品質が下がります。もしこだわりたければ、GLSLでは浮動小数点を使うそれぞれの変数ごとに精度を指定できます。一行目の `precision mediump float;` は、全ての浮動小数点型の変数に中レベルの精度を指定しています。より低い精度(`precision lowp float;`)や高い精度(`precision highp float;`)を選ぶこともできます。
7. 最後に、そしておそらく一番大切なことですが、GLSLの仕様では変数の自動的な型変換は保証されていません。どういうことでしょう。メーカーは色々な方法でグラフィックカードを高速化しようとしますが、同時に最低限の仕様を満たす必要があります。自動的な型変換はこの最低限の仕様には含まれていません。上のサンプルでは `vec4` 型は浮動小数点精度を持っているので、`float` 型の値が割り当てられることになっています。コードの一貫性を保ち、真っ白な画面で何時間もデバッグするのを避けるために、浮動小数点型の値には小数点(`.`)を使うことを習慣にしましょう。下記のようなコードはうまく動くとは限りません。
(訳注:gl_FragColorはGLSL1.3から非推奨になりました。以降のバージョンでは開発者が自由に名前をつけることができます。この本のサンプルは1.2以前のバージョンで書かれているため、gl_FragColorを出力用の変数として用います)
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}さて、冒頭のサンプルで大事なことはほとんど説明しました。そろそろコードをクリックして、学んだことをひととおり試してみましょう。エラーが起きたときはプログラムがコンパイルに失敗し真っ白な画面が表示されます。
次のような例を試すと面白いでしょう。
* 浮動小数点型を整数型に置き換えてみましょう。グラフィックカードによって、そのまま動くこともエラーになることもあります。
* 6行目をコメントアウトして好きな値を関数に割り当ててみましょう。
* 特定の色を返す別の関数を用意して `main()` 関数の中で使ってみましょう。ヒント:下記のコードは赤色を返す関数です。
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* `vec4`型の変数を生成するには幾つかの方法があります。他の方法を見つけてみましょう。下記はひとつの例です。
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);この章のサンプルはさほど面白くはありませんが、キャンバス上のピクセルを全て同じ色に変える、最も基礎的な例です。この後に続く章ではピクセルの色を2種類の入力、空間(画面上のピクセルの位置)と時間(ページがロードされてから経過した秒数)を使って変化させる方法を学びます。
---
02/README Pl (02/README-pl.md)
Witaj świecie!
Zazwyczaj przykład "Hello world!" stanowi pierwszy krok przy nauce nowego języka. Jest to prosty jednolinijkowy program, który zwraca pełną entuzjazmu wiadomość powitalną i tym samym zapowiada nadchodzące przygody.
W świecie GPU renderowanie tekstu jest jednak zbyt skomplikowanym zadaniem dla żółtodzioba. Zamiast tego wybierzemy jasny, serdeczny kolor by wykrzyczeć naszą ekscytację!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Jeżeli czytasz tę książkę w przeglądarce, powyższy blok kodu jest interaktywny. Oznacza to, że możesz edytować dowolną linijkę kodu w celach eksploracyjnych. Shader kompiluje się na bieżąco, więc zmiany widoczne będą natychmiast. Spróbuj pozmieniać wartości w linijce 8.
Choć kod jest prosty, to możemy wyciągnąć z niego ważne wnioski:
1. Podobnie jak w C, GLSL ma jedną funkcje main. Pod koniec zwraca ona kolor.
2. Finalny kolor piksela przypisywany jest do zarezerowanej zmiennej globalnej gl_FragColor.
3. Ten C-podobny język ma wbudowane zmienne (jak gl_FragColor), funkcje i typy. W aktualnym przykładzie występuje jedynie typ vec4, oznaczający czterowymiarowy wektor zmiennoprzecinkowy (ang. "float vector"). Później zobaczymy również takie typy jak vec3, vec2 oraz znajome float, int i bool.
4. Patrząc na typ vec4, możemy wywnioskować, że jego cztery argumenty odnoszą się do kanałów CZERWONEGO, ZIELONEGO, NIEBIESKIEGO i ALPHA. Widać też, że jego wartości są znormalizowane, więc znajdują się w zakresie od 0.0 do 1.0. Później zobaczymy, jak normalizowanie wartości pomaga w mapowaniu wartości między zakresami.
5. Kolejną ważną C-podobną własnością w tym przykładzie jest obecność makr preprocessora. Dzięki nim można definiować zmienne globalne za pomocą #define oraz operacje warunkowe za pomocą #ifdef ("if defined"), #ifndef ("if not defined") i #endif. Wszystkie makra zaczynają się od płotka # i ewaluowane są podczas procesu prekompilacji poprzedzającego kompilację. W naszym powyższym przykładzie linijka 2 kompilowana jest tylko wtedy, gdy zmienna GL_ES jest zdefiniowana (co występuje na urządzeniach mobilnych i w przeglądarkach).
6. Typy zmiennoprzecinkowe są kluczowe w shaderach, więc ich poziom precyzji (ang. precision) ma ogromne znaczenie. Niższa precyzja oznacza szybsze renderowanie, ale kosztem jakości. Możesz być wybredny i określać precyzję każdej zmiennej zmiennoprzecinkowej z osobna. W linijce 2 (precision mediump float;) ustawiamy średnią precyzję zmiennych zmiennoprzecinkowych ("mediump", bo "medium precision"). Możemy też ustawić ją jako niską (precision lowp float;) lub wysoką (precision highp float;).
7. Ostatni i chyba najważniejszy szczegół specyfikacji GLSL: nie ma gwarancji, że zmienne będą automatycznie castowane (np. z int do float przy dzieleniu liczby 5 przez 2). Producenci GPU mogą stosować przeróżne optymalizacje w kartach graficznych, ale muszą przy tym przestrzegać pewnych wytycznych. Automatyczne castowanie nie jest jednym z nich. W naszym przykładzie vec4 ma precyzję zmiennoprzecinkową i dlatego jego argumenty wymagają floatów. Przyzwyczaj się do stawiania kropek (.) we floatach (1. lub 1.0, a nie 1), jeżeli nie chcesz spędzić godzin przy debugowaniu. Poniższy kod nie zawsze będzie, zatem, działał:
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}Czas na ćwiczenia! Pamiętaj, że w wypadku błędu kompilacji pokaże się informacje o błędzie i linijce w której wystąpił, a kanwa zmieni kolor na biały.
* Spróbuj zamienić floaty na inty. Jeśli kod się nie kompiluje, to widocznie twoja karta graficzna tego nie toleruje
* Zakomentuj linię 8
* Stwórz osobną funkcję, która zwraca dowolny kolor i użyj jej w main(). Wskazówka: poniższy kod zwraca kolor czerwony:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* Jest wiele sposobów tworzenia typu vec4 - spróbuj je znaleźć. Jeden z nich wygląda tak:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Choć przykład ten nie jest zbyt ekscytujący, stanowi ważną podstawę. W następnych rozdziałach zobaczymy, jak zmienić kolor piksela z pomocą inputu przestrzennego (położenie piksela na ekranie) i temporalnego (okres czasu od momentu załadowania się strony).
---
02/README Pt (02/README-pt.md)
Olá mundo
Geralmente o exemplo "Olá mundo" é o primeiro passo ao aprender uma nova linguagem. É um programa de uma linha de código que retorna uma entusiástica mensagem de boas vindas e declara as oportunidades que nos aguarda.
No mundo da GPU, renderizar texto é uma tarefa muito complicada para ser o primeiro passo, portanto usaremos uma cor brilhante cor de boas-vindas para representar nosso entusiasmo!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Se você está lendo este livro em um navegador, o bloco de código acima é interativo. Isso significa que você pode clicar e modificar qualquer parte do código em que você quiser explorar sua funcionalidade. As mudanças serão atualizadas imediatamente graças à arquitetura da GPU que compila e substitui os shaders instantaneamente. Experimente modificar os valores da linha 8.
Embora essas simples linhas de código não parecem ser tão interessantes, podemos inferir muita coisa sobre elas:
1. Linguagem de shader Shader Language possui uma função principal - main - que retorna uma cor. Isto é similar a C.
2. A cor final do pixel é atribuída pela variável global reservada gl_FragColor.
3. Essa linguagem similar a C tem variáveis nativas (como gl_FragColor), funções e tipos. Nesse caso vemos vec4, que representa um vetor em quatro dimensões com precisão de ponto flutuante. Mais adiante veremos outros tipos como vec3 e vec2 além dos populares: float, int and bool.
4. Se observarmos o tipo vec4 podemos inferir que os quatro argumentos correspondem aos canais RED (vermelho), GREEN (verde), BLUE (azul) e ALPHA (alfa). Também vemos que esses valores são normalizados, ou seja, eles vão de 0.0 a 1.0. Mais tarde aprenderemos como normalizar valores facilitam o seu mapeamento entre variáveis.
5. Outra importante característica de C que podemos ver neste exemplo é a presença de macros de preprocessador. Macros são parte do passo de pré-compilação. Com eles podemos definir variáveis globais (com #define) e fazer operações condicionais básicas (com #ifdef e #endif). Todos os comandos de macros começam com uma hashtag (#). A pré-compilação acontece logo antes da compilação e copia todas as chamadas para condicionais #define e checa #ifdef (está definido) e #ifndef (não está definido). Em nosso exemplo "Olá mundo" acima nós inserimos na linha 2 se GL_ES for definido, o que provavelmente acontece quando o código é compilado em celulares e navegadores.
6. Tipos float são vitais em shaders, então o nível de precisão é crucial. Menor precisão maior a velocidade de renderização, porém a qualidade é afetada. Você pode ser meticuloso e especificar a precisão para cada variável que usa ponto flutuante. Na primeira linha (precision mediump float;) estamos ajustando todos os floats para precisão média. Mas podemos ajustá-las para baixa (precision lowp float;) ou alta (precision highp float;) também.
7. O final, e talvez mais importante detalhe é que a especificação da GLSL não garante que as variáveis serão automaticamente convertidas. O que isso significa? Fabricantes têm soluções para acelerar os processos da placa gráfica mas eles são forçados a garantir as especificações mínimas. Conversão automática não está entre delas. Em nosso exemplo "olá mundo!" vec4 tem precisão de ponto flutuante e por isso ele espera ser convertido com floats. Se você quiser escrever código de maneira consistente e não gastar horas investigando telas em branco, se acostume a colocar um ponto (.) em seus flutuantes (floats). Esse tipo de código às vezes não funcionará:
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}Agora que descrevemos os elementos mais relevantes em nosso programa "Olá mundo!", chegou a hora de clicar no bloco de código e começar a aplicar o que aprendemos até agora. Você notará que, quando houver um erro, o programa não compilará e renderizará uma tela em branco. Têm algumas coisas interessantes que você pode tentar, por exemplo:
* Tente substituir flutuantes (floats) por números inteiros (integers), pode ser que a sua placa gráfica não ofeça suporte.
* Tente comentar a linha 8 e não atribuir nenhum valor nessa função.
* Tente escrever outra função à parte que retorna uma cor específica e a utilize em main(). Dica: aqui está o código para uma função que retorna a cor vermelho:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* Existem múltiplas maneiras diferentes para construir tipos vec4, tente descobrir outras maneiras. Este é um deles:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Apesar deste exemplo não ser tão empolgante, ele é o exemplo mais básico - estamos modificando todos os pixels da tela ao mesmo tempo e atribuindo a eles a mesma cor. No próximo capítulo veremos como mudar as cores dos pixels usando dois tipos de entrada (input): espaço (a posição do pixel na tela) e tempo (o número de segundos desde que a página foi carregada).
---
02/README Tr (02/README-tr.md)
Merhaba Dünya
Genellikle "Merhaba dünya!" örneği yeni bir dili öğrenmenin ilk adımıdır. Hevesli bir karşılama mesajı veren ve önünüzdeki fırsatları ilan eden basit, tek satırlık bir programdır.
GPU diyarında metin render etmek (ekrana çizdirmek) ilk adım için aşırı karmaşık bir görevdir, bunun yerine hevesimizi haykırmak için parlak bir karşılama rengi seçeceğiz!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Bu kitabı bir tarayıcıda okuyorsanız, önceki kod bloğu interaktiftir. Yani keşfetmek istediğiniz kodun herhangi bir yerine tıklayıp değiştirebilirsiniz. Değişiklikler, shader'ları anında derleyen ve değiştiren GPU mimarisi sayesinde hemen güncellenecektir. 8. satırdaki değerleri değiştirerek deneyin.
Bu basit kod satıraları çok gibi görünmese de, onlardan önemli bilgiler çıkarabiliriz:
1. Shader Dili, sonunda bir renk döndüren tek bir main fonksiyonuna sahiptir. Bu C'ye benzer.
2. Son piksel rengi, ayrılmış global değişken gl_FragColor'a atanır.
3. Bu C-tadındaki dil, yerleşik değişkenlere (gl_FragColor gibi), fonksiyonlara ve türlere sahiptir. Bu durumda, kayan nokta hassasiyetli dört boyutlu bir vektör anlamına gelen vec4 ile tanıştık. Daha sonra vec3 ve vec2 gibi türleri ve popüler olanları göreceğiz: float, int ve bool.
4. vec4 türüne yakından bakarsak, dört argümanın KIRMIZI (RED), YEŞİL (GREEN), MAVİ (BLUE) ve ALFA (ALPHA) kanallarına yanıt verdiğini çıkarabiliriz. Ayrıca bu değerlerin normalize edildiğini, yani 0.0 ile 1.0 arasında gittiklerini görebiliriz. Daha sonra, değerleri normalize etmenin değişkenler arasında değerleri eşlemeyi (map) nasıl kolaylaştırdığını öğreneceğiz.
5. Bu örnekte görebileceğimiz bir diğer önemli C özelliği de önişlemci makrolarının varlığıdır. Makrolar, derleme öncesi bir adımın parçasıdır. Onlarla global değişkenleri #define etmek ve bazı temel koşullu işlemleri (#ifdef ve #endif ile) yapmak mümkündür. Tüm makro komutları bir hashtag (#) ile başlar. Derleme öncesi işlem, derlemeden hemen önce gerçekleşir ve tüm #defines çağrılarını kopyalar ve #ifdef (tanımlıysa) ve #ifndef (tanımlı değilse) koşullarını kontrol eder. Yukarıdaki "merhaba dünya!" örneğimizde, 2. satırı yalnızca GL_ES tanımlıysa ekliyoruz; bu da çoğunlukla kod mobil cihazlarda ve tarayıcılarda derlendiğinde olur.
6. Float türleri shader'larda hayati önem taşır, bu nedenle hassasiyet (precision) seviyesi çok önemlidir. Düşük hassasiyet daha hızlı render anlamına gelir, ancak kalite pahasına. Seçici olabilir ve kayan nokta kullanan her değişkenin hassasiyetini belirleyebilirsiniz. İkinci satırda (precision mediump float;) tüm float'ları orta hassasiyete ayarlıyoruz. Ancak bunları düşük (precision lowp float;) veya yüksek (precision highp float;) olarak ayarlamayı da seçebiliriz.
7. Son ve belki de en önemli ayrıntı, GLSL spesifikasyonlarının değişkenlerin otomatik olarak dönüştürüleceğini (cast) garanti etmemesidir. Bu ne anlama geliyor? Üreticilerin grafik kartı işlemlerini hızlandırmak için farklı yaklaşımları vardır ancak minimum spesifikasyonları garanti etmek zorundadırlar. Otomatik dönüştürme bunlardan biri değildir. "Merhaba dünya!" örneğimizde vec4 kayan nokta hassasiyetine sahiptir ve bu nedenle kendisine float atanmasını bekler. İyi ve tutarlı bir kod yazmak ve beyaz ekranlarda hata ayıklamak için saatler harcamak istemiyorsanız, float'larınıza nokta (.) koymaya alışın. Bu tür bir kod her zaman çalışmayacaktır:
void main() {
gl_FragColor = vec4(1,0,0,1); // HATA
}"Merhaba dünya!" programımızın en alakalı unsurlarını tanımladığımıza göre, kod bloğuna tıklayıp öğrendiklerimizi zorlamaya başlamanın zamanı geldi. Hatalarda programın derlenemeyeceğini ve beyaz bir ekran göstereceğini fark edeceksiniz. Denenecek bazı ilginç şeyler var, örneğin:
* Float'ları tam sayılarla (integer) değiştirmeyi deneyin, grafik kartınız bu davranışı tolere edebilir veya etmeyebilir.
* 8. satırı yoruma almayı ve fonksiyona herhangi bir piksel değeri atamamayı deneyin.
* Belirli bir renk döndüren ayrı bir fonksiyon yapmayı ve bunu main() içinde kullanmayı deneyin. İpucu olarak, işte kırmızı bir renk döndüren bir fonksiyonun kodu:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* vec4 türlerini oluşturmanın birden fazla yolu vardır, diğer yolları keşfetmeye çalışın. Aşağıdakilerden biri bunlardan biridir:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Bu örnek çok heyecan verici olmasa da, en temel örnektir - tuvalin (canvas) içindeki tüm pikselleri aynı tam renge değiştiriyoruz. Sonraki bölümde, piksel renklerini iki tür girdi kullanarak nasıl değiştireceğimizi göreceğiz: uzay (pikselin ekrandaki yeri) ve zaman (sayfa yüklendiğinden beri geçen saniye sayısı).
---
02/README Vi (02/README-vi.md)
Hello World
Thông thường thì ví dụ "Hello World!" sẽ là bước đầu tiên khi học một ngôn ngữ lập trình mới. Đó là một chương trình đơn giản, in ra 1 dòng chữ chào đón.
Ở địa-bàn-của-GPU thì việc vẽ các ký tự chữ số là bước khởi đầu có phần quá phức tạp, thay vào đó chúng tôi chọn sẽ tô một màu tươi sáng lên màn hình để chào đón các bạn một cách nồng nhiệt nhất!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Nếu bạn đang đọc trên trình duyệt thì đoạn code phía trên có thể sửa được đấy. Điều đó có nghĩa là bạn có thể sửa bất kỳ phần nào bạn muốn. Thay đổi sẽ được cập nhật ngay lập tức nhờ vào kiến trúc GPU có khả năng biên dịch và cập nhật shader thời gian thực. Hãy thử thay đổi các giá trị ở dòng 8.
Dù chỉ có vài dòng code đơn giản thôi, nhưng ta có thể thu được nhiều thứ từ chúng lắm đấy:
1. Ngôn ngữ Shader có 1 hàm main trả về mã màu. Tương tự như ngôn ngữ C.
2. Màu cuối cùng của mỗi điểm ảnh được lưu vào biến toàn cục là gl_FragColor.
3. Ngôn ngữ nhìn-như-C này có sẵn vài biến (như gl_FragColor), hàm và kiểu dữ liệu. Trong trường hợp này, ta đã được giới thiệu về kiểu vec4 lưu dữ liệu của một vector 4 chiều với độ chính xác của số thực. Sau này ta sẽ thấy các kiểu tương tự như vec3 và vec2 cũng như các kiểu dữ liệu phổ biến khác như: float, int và bool.
4. Nếu tinh ý, bạn sẽ nhận ra vec4 lưu giá trị của 4 kênh đỏ (RED), xanh lá (GREEN), xanh dương (BLUE) và độ trong suốt (ALPHA). Và bạn cũng sẽ nhận thấy các giá trị này đã được chuẩn hoá (normalized), có nghĩa là giá trị chỉ nằm trong khoảng từ 0.0 tới 1.0. Sau này ta sẽ thấy các giá trị được chuẩn hoá sẽ giúp việc ánh xạ từ biến này sang biến kia trở nên dễ dàng như thế nào.
5. Một tính năng quan trọng khác (học từ ngôn ngữ C) ở trong ví dụ này là sự xuất hiện của các macro, chúng sẽ được xử lý ngay trước khi code được biên dịch. Bằng cách sử dụng macro, ta có thể #define các biến toàn cục và một vài thao tác điều kiện cơ bản (với #ifdef và #endif). Tất cả các lệnh macro đều được bắt đầu bằng ký hiệu hashtag (#). Ngay trước khi biên dịch, tất cả các lệnh điều kiện như #ifdef (nếu có macro) và #ifndef (nếu không có macro) sẽ được kiểm tra. Ở ví dụ "Hello World!" này, ta chỉ kiểm tra xem macro GL_ES có tồn tại không mà thôi. Macro này có tồn tại trên hầu hết các thiết bị điện thoại di động và trình duyệt, tức là dòng lệnh số 2 sẽ chỉ có tác dụng trên các nền tảng này.
6. Các kiểu dữ liệu số thực là yếu tố sống còn trong shader, nên việc quy định mức độ chính xác của chúng là tối quan trọng. Độ chính xác thấp đồng nghĩa với việc dựng hình nhanh hơn và đánh đổi lấy chất lượng thấp hơn. Bạn có thể tỉ mỉ điều chỉnh độ chính xác cho từng biến nếu muốn. Ở dòng 2 (precision mediump float;) ta đã quy định mức độ chính xác của toàn bộ các số thực ở mức trung bình. Nhưng cũng có thể giảm xuống mức thấp (precision lowp float;) hoặc tăng lên mức cao (precision highp float;).
7. Điều cuối cùng, có thể là quan trọng nhất, GLSL không đảm bảo các biến sẽ được chuyển đổi sang kiểu phù hợp. Điều đó nghĩa là gì ? Các nhà sản xuất có các cách tiếp cận khác nhau để tăng tốc card xử lý đồ hoạ của riêng họ nhưng đều phải tuân thủ các yêu cầu tối thiểu. Mà tự chuyển đổi kiểu dữ liệu thì không nằm trong số các yêu cầu bắt buộc đó. Trong ví dụ "Hello World!" của ta, vec4 có độ chính xác của số thực nên giá trị mà nó lưu giữ nên có kiểu là float. Nếu bạn muốn viết code có độ ổn định cao mà không phải tốn hàng giờ ngồi debug trước màn hình trắng tinh, thì hãy làm quen với việc viết thêm dấu chấm (.) sau các số thực, nó giúp GPU biết đang phải xử lý một số thực. Đoạn code dưới đây không phải lúc nào cũng chạy đúng:
void main() {
gl_FragColor = vec4(1,0,0,1); // LỖI
}Ta vừa mới mô tả những yếu tố cơ bản nhất trong chương trình "Hello World!", giờ là lúc để tự sửa code và bắt đầu áp dụng những gì mới được học. Bạn sẽ nhận ra nếu có lỗi thì chương trình không thể biên dịch được còn màn hình sẽ hiển thị màu trắng. Hãy thử vài thứ thú vị sau xem sao:
* Thử thay số thực bằng số nguyên, có thể card đồ hoạ của bạn vẫn chấp nhận đó
* Thử comment dòng số 8 và không lưu lại bất kỳ giá trị điểm ảnh nào
* Thử tạo một hàm mới chỉ trả về duy nhất một màu cố định và gọi hàm đó trong hàm main(). Gợi ý, đây là ví dụ một hàm trả về màu đỏ:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}* Có nhiều cách khởi tạo kiểu vec4, sau đây là 1 trong số chúng:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);Dù ví dụ này không hấp dẫn lắm, nhưng nó là ví dụ cơ bản nhất - ta đã đổi màu của toàn bộ canvas sang một màu cố định. Ở chương tới ta sẽ tìm cách đổi màu điểm ảnh dựa theo vị trí của nó và thời điểm mà trang web được load nữa.
---
02/SUMMARY (02/SUMMARY.md)
Usually the "Hello world!" example is the first step to learning a new language. In GPU-land rendering text is an overcomplicated task for a first step, instead we'll choose a bright welcoming color to shout our enthusiasm!
---
03/README (03/README.md)
Uniforms
So far we have seen how the GPU manages large numbers of parallel threads, each one responsible for assigning the color to a fraction of the total image. Although each parallel thread is blind to the others, we need to be able to send some inputs from the CPU to all the threads. Because of the architecture of the graphics card those inputs are going to be equal (uniform) to all the threads and necessarily set as read only. In other words, each thread receives the same data which it can read but cannot change.
These inputs are called uniform and come in most of the supported types: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D and samplerCube. Uniforms are defined with the corresponding type at the top of the shader right after assigning the default floating point precision.
#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load
You can picture the uniforms like little bridges between the CPU and the GPU. The names will vary from implementation to implementation but in this series of examples I’m always passing: u_time (time in seconds since the shader started), u_resolution (billboard size where the shader is being drawn) and u_mouse (mouse position inside the billboard in pixels). I’m following the convention of putting u_ before the uniform name to be explicit about the nature of this variable but you will find all kinds of names for uniforms. For example ShaderToy.com uses the same uniforms but with the following names:
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iTime; // shader playback time (in seconds)Enough talking, let's see the uniforms in action. In the following code we use u_time - the number of seconds since the shader started running - together with a sine function to animate the transition of the amount of red in the billboard.
<div class="codeAndCanvas" data="time.frag"></div>
As you can see GLSL has more surprises. The GPU has hardware accelerated angle, trigonometric and exponential functions. Some of those functions are: sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() and clamp().
Now it is time again to play with the above code.
* Slow down the frequency until the color change becomes almost imperceptible.
* Speed it up until you see a single color without flickering.
* Play with the three channels (RGB) in different frequencies to get interesting patterns and behaviors.
gl_FragCoord
In the same way GLSL gives us a default output, vec4 gl_FragColor, it also gives us a default input, vec4 gl_FragCoord, which holds the screen coordinates of the pixel or screen fragment that the active thread is working on. With vec4 gl_FragCoord, we know where a thread is working inside the billboard. In this case we don't call it uniform because it will be different from thread to thread, instead gl_FragCoord is called a varying.
<div class="codeAndCanvas" data="space.frag"></div>
In the above code we normalize the coordinate of the fragment by dividing it by the total resolution of the billboard. By doing this the values will go between 0.0 and 1.0, which makes it easy to map the X and Y values to the RED and GREEN channel.
In shader-land we don’t have too many resources for debugging besides assigning strong colors to variables and trying to make sense of them. You will discover that sometimes coding in GLSL is very similar to putting ships inside bottles. It is equally hard, beautiful and gratifying.
Now it is time to try and challenge our understanding of this code.
* Can you tell where the coordinate (0.0, 0.0) is in our canvas?
* What about (1.0, 0.0), (0.0, 1.0), (0.5, 0.5) and (1.0, 1.0)?
* Can you figure out how to use u_mouse, knowing that the values are in pixels and NOT normalized values? Can you use it to move colors around?
* Can you imagine an interesting way of changing this color pattern using u_time and u_mouse coordinates?
After doing these exercises you might wonder where else you can try your new shader-powers. In the following chapter we will see how to make your own shader tools in three.js, Processing, and openFrameworks.
---
03/Notes (03/notes.md)
#### Scott
* For these uniforms:
uniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load…what determines these variable names? Are these set by you, so I could change them, or are these determined by the graphics card, GLSL, or something else?
* This list of GLSL-supported functions is super. Maybe you could link to a comprehensive list here, too.
• In "Slow down the frequency until the color change becomes almost imperceptible" took me a minute to figure out the right place to do that. I ended up writing:
gl_FragColor = vec4( abs(sin(u_time * 0.4)) ,0.0,0.0,1.0);I mention it because I felt this is the first place where you might lose some people. Consider giving a little more guidance with this instruction.
* So gl_FragCoord is a varying, so it doesn’t have to be declared. But uniforms do have to be declared before you can use them? Is that right?
* Starting in this example, I need to be told what the components of a vec4() are. It looks like you are using vec4.x and vec4.y. But you didn’t introduce these. What other options do I have?
* Also I would want the fields of gl_FragCoord explained. You used gl_FragCoord.xy but didn’t explain it. I assume it’s a vec2 with the x and y values, of course.
Come to think of it, it is crazy that you can write:
gl_FragCoord.xy / u_resolution
and have that return a vec2! In Processing you have to use something like vector.divide(). Maybe it would be worth explaining a bit what your line of code above does? Does it really divide both the X and Y values by u_resolution?
NOTES:
uniform vec2 u_resolution; // vec2(500.0,500.0)
uniform vec2 u_mouse;
uniform float u_time;
void main() {
// X Y
// gl_FragCoord.xy = vec2(0.0,0.0);
// / /
// vec2(500.0,500.0);
vec2 st = gl_FragCoord.xy/u_resolution;
// ST -> vec2(0.0,0.0) to vec2(1.0,1.0)
// RED GREEN BLUE ALPHA
gl_FragColor = vec4(st.x, st.y, 0.0, 1.0);
}
Nicolas
* >You can picture the uniforms like little bridges between the CPU and the GPU
a 'one-way'' bridges that is :) I translated it to:
>We can picture the uniforms as small one-way bridges from the CPU (our main program) to the GPU (where the shader will be executed).
* >In the same way GLSL gives us a default output, ``vec4 gl_FragColor`, it also gives us a default input, `vec4 gl_FragCoord`,
which holds the screen coordinates of the pixel or screen fragment that the active thread is working on.
I developped a bit more to disambiguate pixel & fragment, I think it's an important step, the first time we meet the word fragment _for real_
* I've stressed the fact that gl_FragCoord is implicitly declared so that people don't panic when they don't find it in the new code sample :)
also those are reserved names, so I stressed the fact that you can't use them as var names in your custom code.
---
03/README De (03/README-de.md)
Uniforms
Wir haben bereits gesehen, wie die GPU eine große Anzahl von parallelen Aufgaben (engl. Threads) verwaltet. Jeder Thread übernimmt die Berechnung des Farbwerts für einen Pixel, also für einen kleinen Teil der gesamten Grafik. Obwohl er dabei blind für die anderen Threads ist, muss es einen Weg geben, wie die CPU bestimmte Daten an alle Threads übermitteln kann. Aufgrund der Architektur einer Grafikkarte müssen diese Daten für alle ausgeführten Threads einheitlich (engl. uniform) und nur lesbar (engl. read-only) sein. Jeder Thread erhält also die gleichen Daten, die er nicht verändern kann.
Man bezeichnet diese Daten deshalb als `uniform`. Es gibt sie in den wichtigsten Datentypen, die GLSL unterstützt: `float`, `vec2`, `vec3`, `vec4`, `mat2`, `mat3`, `mat4`, `sampler2D` und `samplerCube`. Uniforms werden gemeinsam mit ihrem jeweiligen Datentyp am Anfang eines Shader-Programms definiert, sofern man darauf Bezug nehmen möchte. Dies geschieht in der Regel gleich nachdem man die gewünschte Genauigkeit für alle Fließkommaoperationen in dem Programm festgelegt hat.
#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Groesse der Malfläche (canvas) in Pixeln (Breite, Hoehe)
uniform vec2 u_mouse; // Mausposition über der Malfleache in Pixeln (X, Y)
uniform float u_time; // Zeit in Sekunden seit dem Start des Bildaufbaus
Man kann sich Uniforms als eine Brücke zwischen der CPU und der GPU vorstellen. Ihre Bezeichnung hängt von der jeweiligen Implementation der GLSL-Umgebung ab. Im Rahmen dieses Buches verwenden wir die folgenden Namen: `u_time` , `u_resolution` und `u_mouse`. Wir folgen damit der Konvention, die Variablennamen jeweils mit dem Präfix `u_` beginnen zu lassen, damit sie im Programmcode deutlich als Uniforms erkennbar sind. In anderen Umgebungen für die Entwicklung von Shadern tragen sie andere Namen, wie beispielsweise bei ShaderToy.com. Dort heißt es:
uniform vec3 iResolution; // Groesse der Malflaeche
uniform vec4 iMouse; // Mausposition
uniform float iTime; // Zeit seit dem StartAber nun genug geredet. Lass uns die Uniforms in Aktion betrachten. Der folgende Programmcode nutzt `u_time` - die Anzahl der Sekunden, seitdem der Shader gestartet wurde - in Verbindung mit einer Sinus-Funktion, um die Intensität der Rotfärbung der Malfläche pulsieren zu lassen.
<div class="codeAndCanvas" data="time.frag"></div>
Wie Du siehst, hält GLSL noch einige Überraschungen bereit. Die GPU unterstützt in der Hardware realisierte Winkel-, Trigonometrie- und Exponential-Funktionen. Hier einige dieser Funktionen in der Übersicht: `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `pow()`, `exp()`, `log()`, `sqrt()`, `abs()`, `sign()`, `floor()`, `ceil()`, `fract()`, `mod()`, `min()`, `max()` sowie `clamp()`.
Nun ist es an der Zeit, mit dem obigen Shader zu experimentieren.
* Versuche, die Frequenz zu verlangsamen, mit der sich die Rotfärbung ändert, bis dieser Prozess kaum noch wahrnehmbar ist.
* Beschleunige die Frequenz, bis man fast nur noch eine Farbe sieht.
* Experimentiere mit periodischen Änderungen auch in den anderen Farbkanälen (Grün und Blau), so dass sich interessante Farbspiele und Muster ergeben.
gl_FragCoord
So, wie GLSL das Resultat eines Shader-Durchlaufs standardmäßig in der Variable `vec4 gl_FragColor` erwartet, liefert es uns auch standardmäßig einen Eingabewert: Die Koordinate des jeweils zu bearbeitenden Bildpunkts in der Variable `vec4 gl_FragCoord`. Im Englischen spricht man in diesem Zusammenhang auch von einem screen fragment, weil es sich nur um einen kleinen Teil der Zeichenfläche handelt, eben ein „Fragment“. Diese Variable kann man nicht als `uniform` bezeichnen, weil ihr Inhalt bzw. ihr Wert mit jedem Shader-Durchlauf variiert. Man spricht deshalb auch von einem varying.
<div class="codeAndCanvas" data="space.frag"></div>
Im obigen Programmcode normalisieren wir zunächst die Koordinate des zu bearbeitenden Fragments, indem wir sie durch die Auflösung der Zeichenfläche teilen. Auf diese Weise bilden wir die X- und Y-Ordinate jeweils auf den Wertebereich zwischen `0.0` und `1.0` ab. Das erleichtert es uns, diese Ordinaten auf Farbwerte für den Rot- und den Grün-Kanal zu übertragen. Schließlich müssen sich diese Farbwerte in GLSL auch immer jeweils zwischen `0.0` und `1.0` bewegen.
In der Welt der Shader-Programmierung haben wir nicht so viele Möglichkeiten zum Debugging, abgesehen davon, dass wir dem gerade berechneten Bildpunkt intensive Farbtöne zuweisen können. Das entstehende Bild lässt dann Rückschlüsse auf die Abläufe innerhalb des Shaders zu. Du wirst im Laufe dieses Buches entdecken, dass die Shader-Programmierung manchmal dem Versuch gleicht, ein Modellschiff in eine Flasche zu pressen. Denn das ist gleichermaßen schwierig, aber auch schön anzusehen und in jedem Fall lohnend.
Jetzt ist es an der Zeit für eine kleine Herausforderung in Bezug auf das Verständnis des obigen Programmcodes.
* Kannst du ausmachen, wo sich die Koordinate `(0.0,0.0)` innerhalb unserer Zeichenfläche befindet?
* Und wo liegen wohl die Koordinaten `(1.0,0.0)`, `(0.0,1.0)`, `(0.5,0.5)` und `(1.0,1.0)`? Die Farben der jeweiligen Bildpunkte verraten es Dir!
* Gelingt es Dir, die aktuelle Mausposition aus dem Uniform `u_mouse` einzubeziehen? Denke daran, dass sich die Angaben in diesem Uniform auf Pixel beziehen und zunächst nicht normalisiert sind. Kannst Du den Programmcode so gestalten, dass die erzeugten Farben auf die Mausbewegung reagieren?
* Fällt Dir ein Weg ein, wie man die Farbgestaltung auf interessante Weise durch die Einbeziehung von `u_time` und `u_mouse` dynamisieren kann?
Nach all diesen Übungen fragst Du Dich vielleicht, wo Du Deine neuen Shader-Kenntnisse sonst noch ausprobieren kannst. Im folgenden Kapitel zeigen wir, wie man Shader in three.js, Processing und openFrameworks zum Laufen bringt.
---
03/README Es (03/README-es.md)
Uniforms
Hasta ahora hemos visto como la GPU maneja grandes números de threads en paralelo, cada uno responsable de asignar un color a una fracción de la pantalla. A pesar de que cada thread no conoce a los otros, necesitamos poder enviarle valores de entrada desde la CPU a todos los threads. Debido a la arquitectura de la GPU todos esos valores van a ser iguales (uniform) para todos los threads y de sólo lectura. En otras palabras, cada thread recibe las misma información y puede leerla pero no modificarla.
Estas entradas se llaman `uniform` y vienen en diferentes tipos: `float`, `vec2`, `vec3`, `vec4`, `mat2`, `mat3`, `mat4`, `sampler2D` y `samplerCube`. Los uniforms son definidos con sus correspondientes tipos, al principio del código, luego de definir la precisión del punto flotante.
#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load
Podemos imaginar que los uniforms son como pequeños puentes entre la CPU y la GPU. Los nombres varían dependiendo de cada implementación, en esta serie de ejemplos estoy usando `u_time` (tiempo en segundos desde que shaders comenzó a correr), `u_resolution` (el tamaño de la ventana donde se está dibujando el shader) y `u_mouse` (la posición del mouse dentro de la ventana en pixeles). Estoy siguiendo la convención de utilizar `u_` antes del nombre del uniform, para ser explícito respecto a la naturaleza de la variable, pero encontrarás diferentes nombre de uniforms. Por ejemplo ShaderToy.com utiliza las mismas uniforms pero con los siguientes nombres:
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iTime; // shader playback time (in seconds)Ya hemos hablado mucho, vamos a ver los uniforms en acción. En el código siguiente usamos `u_time` - el número de segundos desde que el shader comenzó a ejecutarse - junto con una función del seno para animar en transición la cantidad de rojo en la pantalla.
<div class="codeAndCanvas" data="time.frag"></div>
Como puedes ver, GLSL tiene más sorpresas. La GPU tiene funciones de ángulo, de trigonometría y exponenciales, que son aceleradas por hardware: `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `pow()`, `exp()`, `log()`, `sqrt()`, `abs()`, `sign()`, `floor()`, `ceil()`, `fract()`, `mod()`, `min()`, `max()` y `clamp()`.
Es hora de jugar con el código de arriba:
* Reduce la frecuencia del cambio hasta que sea prácticamente imperceptible.
* Acelera la frecuencia hasta que dejes de ver el parpadeo.
* Juega con los canales de colores (RGB) en diferentes frecuencias, para obtener patrones y comportamientos interesantes.
gl_FragCoord
De la misma forma que GLSL nos da por default la variable reservada `vec4 gl_FragColor`, también nos da `vec4 gl_FragCoord` que guarda la coordenada del pixel o screen fragment del thread actual. Con `vec4 gl_FragCoord` podemos saber el lugar en la pantalla en el que el thread está actualmente trabajando. En este caso, esta variable no es un `uniform` porque será diferente en cada uno de los threads, las variables que cambian en cada thread, como `gl_FragCoord`, son varying.
<div class="codeAndCanvas" data="space.frag"></div>
En el código de arriba normalizamos la coordenada del fragment, dividiéndolo por la resolución total de la ventana. Una vez que hicimos este proceso, la posición va de 0.0 a 1.0, lo que vuelve mucho más fácil de usar estos valores en los canales RED (rojo) y GREEN (verde).
En el mundo de los shaders no tenemos muchas herramientas para hacer debug más allá de asignar colores e intentar encontrarles el sentido. Muchas veces verás que programar en GLSL es como poner barcos dentro de botellas, cuanto más complicado, más hermoso y gratificante es.
Es hora de poner en práctica los conocimientos aprendidos.
* ¿Podrías decir dónde está nuestro `(0.0,0.0)` en la pantalla?
* ¿Y dónde está `(1.0,0.0)`, `(0.0,1.0)`, `(0.5,0.5)` y `(1.0,1.0)`?
* ¿Puedes imaginar cómo usar `u_mouse` sabiendo que los valores están en pixeles y no están normalizados? ¿Podrías usarlo para mover los colores?
* ¿Te imaginas alguna forma interesante de combinar `u_time` y `u_mouse` para generar patrones ?
Luego de hacer estos ejercicios seguramente te preguntarás que más puedes hacer con los superpoderes que los shaders te dan. En el próximo capítulo veremos como crear tus propios shaders en three.js, Processing y openFrameworks.
---
03/README Fa (03/README-fa.md)
یونیفرم ها
تاکنون دیدیم که GPU چگونه تعداد زیادی ترد موازی را مدیریت میکند. هر کدام از آن ها وظیفه تخصیص رنگ، به جزئی(fragment) از تصویر را دارند. با اینکه هر ترد موازی نسبت به دیگران کور است، اما باید بتوانیم از CPU ورودی هایی به همه ترد ها ارسال کنیم. به دلیل معماری کارت گرافیک، این ورودی ها برای همه ترد ها یکسان هستند و فقط بصورت خواندنی(read only). به عبارتی هر ترد اطلاعات مشابهی را دریافت میکند که میتواند بخواند، اما نمیتواند تغییر دهد.
به این ورودی ها یونیفرم(uniform) گفته میشود. و معمولا به صورت تایپ های مقابل استفاده میشوند: float, vect2, vect3, vect4, mat2, mat3, mat4, sampler2D, samplerCube. یونیفرم ها در بالای شیدر، پس از تعریف دقت نقطه شناور پیشفرض تعریف میشوند.
#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load
یونیفرم ها را میتوان مانند پل های کوچک بین CPU و GPU تصور کرد. نام ها در محیط های مختلف ممکن از تفاوت داشته باشند، اما در این مجموعه از مثال ها همیشه این نام ها ثابتند: u_time(زمان بر حسب ثانیه از شروع شیدر)، u_resolution(اندازه کنوس، جایی که شیدر کشیده میشود) و u_mouse(موقعیت ماوس در داخل کنوس بر حسب پیکسل).
من این قرار داد را دنبال میکنم که قبل یونیفرم ها u_ بگذارم، اما شکل های دیگری هم ممکن است به چشمتان بخورد، مثلا ShaderToy.com از همین یونیفرم ها اما به شکل زیر استفاده کرده است:
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iTime; // shader playback time (in seconds)صحبت کردن کافیست بیایید یونیفرم ها را در عمل ببینیم. در کد زیر ما از u_time استفاده میکنیم. یعنی تعداد ثانیه از زمانی که شیدر شروع شده است را به همراه عملکرد سینوس برای تحریک مقدار قرمز به کار گرفتیم.
<div class="codeAndCanvas" data="time.frag"></div>
همانطور که میبینید GLSL سورپرایز های بیشتری دارد. GPU دارای عملکرد های زاویه شتاب همچنین توابع نمایی و مثلثاتی هست. برخی ازین توابع عبارتند از: sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() و clamp().
اکنون زمان آن است که دوباره با کد بازی کنیم.
* فرکانس را کم کنید تا زمانی که تغییر رنگ نامحسوس شود.
* سرعت را آنقدر زیاد کنید که یک رنگ بدون پرش ببینید.
* با سه کانال RGB در فرکانس های مختلف بازی کنید تا الگو ها و رفتار های جالبی ببینید.
gl_FragCoord
به همان صورت که GLSL به ما یک خروجی vec4 gl_FragColor میدهد، همچنین یک ورودی پیشفرض در نظر دارد. vec4 gl_FragCoord که مختصات پیکسل ها در صفحه و یا قطعه صفحه ای که ترد روی آن کار میکند را نگه میدارد، با vec4 gl_FragCoord، میتوانیم بفهمیم یک ترد کجای بیلبورد کار میکند. در این مورد ما آن را یونیفرم صدا نمیزنیم، بلکه به gl_FragCoord یک varying میگوییم.
<div class="codeAndCanvas" data="space.frag"></div>
در مثال بالا ما با تقسیم gl_FragCoord بر u_resolution مختصات نرمالایز شده(بین 0 و 1) را در st ذخیره میکنیم. با این کار به راحتی میتوان مقادیر x را به قرمز و مقادیر y را به سبز مپ کرد.
در شیدر نویسی منابع زیادی برای دیباگ کردن نداریم، خواهید فهمید که گاهی اوقات کد نویسی در GLSL مانند قراردادن کشتی در داخل بطری است. به همان اندازه سخت زیبا و خوشایند.
اکنون وقت آن است درک خود را در مورد این کد به چالش بکشیم.
* میتوانید بگویید مختصات (0.0،0.0) در کنوس کجاست؟
* در مورد (1.0, 0.0), (0.0, 1.0), (0.5, 0.5), (1.0, 1.0) چطور؟
* آیا میتوانید نحوه استفاده از u_mouse را بیابید و مقدار واقعی در پیکسل نه نرمالایز شده آن را بفهمید؟ از آن برای جابه جایی رنگ ها میتوانید استفاده کنید؟
* آیا میتوانید روش جالبی برای تغییر این الگوی رنگ با استفاده از u_time و u_mouse تصور کنید؟
بعد از انجام این تمرینات، ممکن است کنجکاو باشید که در کجا ها میتوانید از توانایی شیدر نویسی خود استفاده کنید. در قسمت بعد خواهید دید که چگونه میتوان ابزار های شیدر نویسی خود را در three.js, Processing و یا openFrameworks بسازید.
---
03/README Fr (03/README-fr.md)
Uniforms
Jusqu'à présent, nous avons vu comment le GPU gère un ensemble de threads parallèles dont le but est d'assigner la couleur d'une partie de l'image finale.
Bien que chaque thread soit aveugle, nous devons être capables de passer certaines valeurs depuis le CPU vers le GPU et les threads en question.
Du fait de l'architecture des cartes graphiques, ces valeurs vont devoir être également (ou uniform-ément) distribuées sur tous les threads et - comme décrit au chapitre 1 - utilisées en lecture seule.
Autrement dit, _tous les threads reçoivent les mêmes données, chacun peut les lire mais pas les modifier_.
Ces données s'appellent des uniform et peuvent prendre les types suivants : float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D et samplerCube.
Les uniforms se définissent généralement en haut du shader, juste après avoir défini la précision des floats (et autres macros de prétraitement).
#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // taille du Canvas (x:largeur en pixels, y:hauteur en pixels)
uniform vec2 u_mouse; // position de la souris (x,y) sur le canvas en pixels
uniform float u_time; // temps écoulé depuis le lancement du shader
On peut se représenter les uniforms comme de petits ponts à sens unique allant du CPU (notre programme principal) au GPU (là où sera exécuté le shader).
Les noms peuvent varier selon les implémentations et les plateformes mais dans les exemples suivants, nous utiliserons toujours : u_time (le temps écoulé depuis le lancement du shader),u_resolution (la taille du canvas sur lequel le shader est exécuté) et u_mouse (la position de la souris à l'intérieur du canvas).u_
Le fait de préfixer les noms des uniforms par est une convention de nommage assez répandue, ça permet de reconnaître facilement le type de cette variable mais ce n'est pas une obligation.
Par exemple ShaderToy.com utilise les mêmes uniforms avec les noms suivants :
uniform vec3 iResolution; // taille du canvas (en pixels)
uniform vec4 iMouse; // position de la souris. xy: courant, zw: au click
uniform float iTime; // temps écoulé depuis le lancement du shader (en secondes)Notez qu'ils utilisent un i au lieu de notre u_.
Assez parlé, voyons ce que les uniforms peuvent faire.
Dans l'exemple suivant, nous utilisons l'uniform la valeurs absolue (abs(valeur)) d'une fonction de sinus (sin(valeur)) qui prend u_time - le temps écoulé Depuis le lancement du shader - comme argument pour animer la quantité de rouge que nous dessinons sur le canvas.
La fonction de sinus attend un angle comme argument, en utilisant le temps (valeur qui ne cesse de croître), on obtient une valeur qui va osciller infiniment entre -1. et 1..0.
La valeur absolue d'une fonction de sinus sera quant à elle toujours comprise entre et 1. donc notre valeur de rouge oscillera entre 0. et 1..
<div class="codeAndCanvas" data="time.frag"></div>
On peut constater que ça va vite (par rapport au même traitement sur le CPU), cela vient de l'accélération matérielle.
En effet, au chapitre 1 nous avons vu que les GPU implémentent parfois l'accélération matérielle de certaines opérations, certaines fonctions trigonométriques telles que :
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() er clamp(),
sont donc exécutées _matériellement_ et peuvent aller très (très) vite.
Essayons de jouer avec le code ci dessus.
* Ralentissez la fréquence jusqu'à ce que le changement de couleur deviennent imperceptible.
* Accélérez la fréquence jusqu'à voir une couleur unique sans clignotement.
* Donnez des fréquences différentes aux trois canaux (RGB) pour obtenir des motifs et des comportements intéressants.
gl_FragCoord
De la même manière que la fonction main() du shader expose la variable de sortie : vec4 gl_FragColor, elle nous donne accès à une variable d'entrée vec4 gl_FragCoord
qui contient les coordonnées à l'écran du pixel.
Ce pixel s'appelle en fait un screen fragment, qui donne son nom au fragment shader.
Le screen fragment ou plus simplement fragment est le _pixel en train d'être traité par le thread_.
La variable vec4 gl_FragCoord, nous donne donc accès à l'emplacement _physique_ (à l'écran) du pixel sur lequel le thread est en train de travailler.
Cette variable n'est pas une uniform puisqu'elle ne conserve pas la même valeur d'un thread à l'autre, chaque pixel ayant par définition des coordonnées uniques.
La variable gl_FragCoord s'appelle varying puisqu'elle va varier d'un thread sur l'autre, c'est la seconde _famille_ de variables qu'on peut utiliser dans un shader.
Cette variable est déclarée implicitement dans les _vertex-shader_ et passée systématiquement à notre fragment-shader, autrement dit, elle est toujours là mais inutile de la chercher dans le code ci dessous.
Deuxième chose importante, gl_FragColor, gl_FragCoord et tous les noms de fonctions (sin(), abs(), etc.) sont des noms réservés ; on ne peut pas s'en servir pour créer nos variables.
<div class="codeAndCanvas" data="space.frag"></div>
Dans le code ci-dessus, nous normalisons les coordonnées du fragment en les divisant par la taille du canvas.
En normalisant les coordonnées, elles vont se retrouver comprises entre 0.0 et 1.0 ce qui permet de mapper facilement les valeurs X et Y du fragment vers les canaux rouges et verts (R et G) de la couleur de sortie (gl_FragColor).
Au pays des shaders, nous avons peu de moyen de débugger une application à part assigner des valeurs criardes aux fragments et essayer de comprendre ce qui se passe.
Vous découvrirez que parfois, coder un shader c'est comme de fabriquer un tout petit bateau dans une bouteille, c'est dur, c'est beau et c'est gratifiant.
Voyons ce que nous avez appris et compris du code.
* Pouvez dire où se trouvent les coordonnées XY (0.0, 0.0) sur notre canvas ?
* A votre avis où se trouvent les coordonnées(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) et (1.0, 1.0) ?
* Pouvez vous déduire comment utiliser l'uniform u_mouse sachant que ses valeurs sont passées au shader en _pixels_ et ne sont pas normalisées ?
* Pouvez vous utiliser u_mouse pour changer les couleurs ?
* Pouvez vous inventer une manière intéressante de combiner u_time et u_mouse pour créer un motif ?
Après ces petits exercices, vous vous demandez sans doute où exercer vos nouveaux talents.
Au chapitre suivant, nous verrons comment fabriquer nos propres outils dans Three.js, Processing et OpenFrameworks.
---
03/README Gr (03/README-gr.md)
Uniforms
Μέχρι τώρα έχουμε δει πως η GPU διαχειρίζεται μεγάλο πλήθος από παράλληλα threads (νήματα), όπου το καθένα είναι υπεύθυνο να αναθέσει σε κάθε τμήμα της τελικής εικόνας το χρώμα του. Αν και κάθε παράλληλο thread είναι τυφλό προς τα υπόλοιπα, χρειάζεται να μπορούμε να στείλουμε κάποιες εισόδους από τη CPU (ΚΜΕ) σε όλα τα threads. Εξ' αιτίας της αρχιτεκτονικής της κάρτας γραφικών, αυτές οι έισοδοι θα είναι ίδιες (uniform - ομοιόμορφες) για όλα τα threads, και κατ' ανάγκη είναι μόνο για ανάγνωση (read only). Με άλλα λόγια, κάθε thread παίρνει τα ίδια δεδομένα τα οποία μπορεί να διαβάσει αλλά όχι και να αλλάξει.
Αυτές οι είσοδοι λέγονται uniform και υπάρχουν για τους περισσότερους από τους υποστηριζόμενους τύπους: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D και samplerCube. Τα uniforms ορίζονται μαζί με τον αντίστοιχο τύπο στην κορυφή του shader, αμέσως μετά την ανάθεση της ακρίβειας κινητής υποδιαστολής (default floating point precision).
#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Μέγεθος καμβά (πλάτος, ύψος)
uniform vec2 u_mouse; // θέση του mouse σε συντεταγμένες οθόνης (σε pixels)
uniform float u_time; // Χρόνος σε δευτερόλεπτα (seconds) από τη φόρτωση της σελίδας
Μπορούμε να φανταστούμε τα uniforms σαν μικρές γέφυρες ανάμεσα στην CPU και τη GPU. Τα ομόματα θα διαφέρουν από υλοποίηση σε υλοποίηση, αλλά σε αυτά τα παραδείγματα πάντα τα περνάω σαν: u_time (χρόνος σε δευτερόλεπτα από τη στιγμή που ξεκίνησε ο shader), u_resolution (μέγεθος του πίνακα όπου ζωγραφίζεται ο shader) και u_mouse (θέση του mouse μέσα στον πίνακα σε pixels). Ακολουθώ τη σύμβαση να προσθέτω u_ πριν από το όνομα του uniform ώστε να είμαι σαφής ως προς τη φύση της μεταβλητής, αλλά θα συναντήσετε όλων των ειδών τα ονόματα για uniforms. Για παράδειγμα, το ShaderToy.com χρησιμοποιεί τα ίδια uniforms αλλά με τα ακόλουθα ονόματα:
uniform vec3 iResolution; // ανάλυση του viewport (περιοχή "θέασης") (σε pixels)
uniform vec4 iMouse; // συντεταγμένες του mouse σε pixel. xy: τρέχουσα θέση, zw: θέση click
uniform float iTime; // χρόνος εκτέλεσης (playback) του shader (σε seconds)Αλλά αρκετά με τα λόγια, ας δούμε τα uniforms στην πράξη. Στον ακόλουθο κώδικα χρησιμοποιούμε το u_time - τον αριθμό δευτερολέπτων από τη στιγμή που ο shaders άρχισε να εκτελείται - σε συνδυασμό με μια ημιτονοειδή συνάρτηση για να δώσουμε κίνηση στη μεταβολή του κόκκινου στον πίνακα.
<div class="codeAndCanvas" data="time.frag"></div>
Όπως βλέπετε, η GLSL έχει κι άλλες εκπλήξεις. Η GPU υποστηρίζει σε hardware συναρτήσεις γωνίας, τριγωνομετρικές και εκθετικές. Μερικές από αυτές τις συναρτήσεις είναι: sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() και clamp().
Ώρα να παίξουμε πάλι με τον παραπάνω κώδικα.
* Επιβραδύνετε τη συχνότητα μέχρι η αλλαγή χρώματος σχεδόν να μη γίνεται αντιληπτή.
* Επιταχύνετέ τη μέχρι να βλέπετε ένα ενιαίο χρώμα που δε θα τρεμοσβήνει (flicker).
* Παίξτε με τα τρία κανάλια (RGB) σε διαφορετικές συχνότητες για να πάρετε ενδιαφέροντες συνδυασμούς και συμπεριφορές.
gl_FragCoord
Κατά τον ίδιο τρόπο που η GLSL μας δίνει μια προκαθορισμένη (default) έξοδο, vec4 gl_FragColor, μας δίνει και μια προκαθορισμένη είσοδο, vec4 gl_FragCoord, η οποία περιέχει τις συντεταγμένες οθόνης του pixel ή τεμάχιου οθόνης - screen fragment στο οποίο δουλεύει το ενεργό thread. Με το vec4 gl_FragCoord, ξέρουμε που δουλεύει ένα thread μέσα στον πίνακα. Σε αυτή την περίπτωση δεν το ονομάζουμε uniform γιατί θα είναι διαφορετικό από το ένα thread στο άλλο, αντίθετα, το gl_FragCoord λέγεται varying.
<div class="codeAndCanvas" data="space.frag"></div>
Στον παραπάνω κώδικα κανονικοποιούμε (normalize) τις συντεταγμένες του fragment (τεμαχίου) διαιρώντας το με τη συνολική ανάλυση του πίνακα. Με αυτό τον τρόπο, οι τιμές θα βρίσκονται μεταξύ 0.0 και 1.0, κάτι που το κάνει εύκολο να αντιστοιχίσουμε τις τιμές X και Y στα κανάλια RED (κόκκινο) και GREEN (πράσινο).
Στον κόσμο των shaders, δεν έχουμε και πολλές δυνατότητες για debugging (διόρθωση σφαλμάτων) πέρα από το να αναθέτουμε έντονα χρώματα σε μεταβλητές και να προσπαθούμε να βγάλουμε άκρη από αυτά. Θα ανακαλύψετε πως κάποιες φορές το να προγραμματίζετε σε GLSL είναι πολύ παρόμοιο με το να φτιάχνει κανείς καραβάκια μέσα σε μπουκάλια. Είναι εξ' ίσου δύσκολο, όμορφο και ευχάριστο.
Ώρα να δοκιμάσουμε να τσεκάρουμε πόσο έχουμε κατανοήσει αυτό τον κώδικα.
* Μπορείτε να βρείτε που στον καμβά μας βρίσκονται οι συντεταγμένες (0.0, 0.0);
* Ή οι συντεταγμένες (1.0, 0.0), (0.0, 1.0), (0.5, 0.5) και (1.0, 1.0);
* Μπορείτε να βρείτε πως να χρησιμοποιήσετε το u_mouse γνωρίζοντας πως οι τιμές είναι σε pixels και ΟΧΙ κανονικοποιημένες; Μπορείτε να το χρησιμοποιήσετε για να κινήστε τα χρώματα;
* Μπορείτε να επινοήσετε έναν ενδιαφέροντα τρόπο να αλλάζετε το χρωματικό συνδυασμό χρησιμοποιώντας τις συντεταγμένες u_time και u_mouse?
Αφού ολοκληρώσετε αυτές τις ασκήσεις, θα αναρωτηθείτε ίσως πού αλλού μπορείτε να χρησιμοποιήστε τις νέες σας shaderοδυνάμεις. Στο επόμενο κεφάλαιο θα δούμε πως να φτιάξετε τα δικά σας εργαλεία shaders σε three.js, Processing, και openFrameworks.
---
03/README Id (03/README-id.md)
Variabel Seragam (Uniform)
Sejauh ini kita telah melihat bagaimana GPU memanajemen thread paralel dalam jumlah yang besar, masing-masing bertanggung jawab untuk menetapkan warna ke sebagian kecil dari total gambar. Meskipun setiap thread itu buta satu sama lain, kita harus bisa untuk mengirim masukan dari CPU untuk seluruh thread. Karena arsitektur dari kartu grafis, masukan ini akan menjadi sama/seragam (uniform) ke semua thread dan harus disetel sebagai hanya baca. Dengan kata lain, setiap utas menerima data yang sama yang dapat dibaca tetapi tidak dapat diubah.
Masukan ini dinamakan uniform dan tersedia di sebagian besar tipe yang didukung: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D and samplerCube. Uniform ditentukan dengan jenis yang sesuai di bagian atas shader tepat setelah menetapkan presisi floating point default.
#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load
Anda bisa menggambarkan uniform seperti jembatan kecil antara CPU dan GPU. Namanya akan bervariasi dari implementasi ke implementasi, tetapi dalam rangkaian contoh ini saya selalu meneruskan: u_time
(waktu dalam detik sejak shader dimulai), u_resolution (ukuran billboard tempat shader ditarik) dan u_mouse (posisi mouse di dalam billboard dalam piksel). Saya mengikuti konvensi meletakkan u_ sebelum nama seragam agar eksplisit tentang sifat variabel ini tetapi Anda akan menemukan semua jenis nama untuk seragam. Misalnya [ShaderToy.com] (https://www.shadertoy.com/) menggunakan seragam yang sama tetapi dengan nama berikut:glsluniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iTime; // shader playback time (in seconds)
Cukup bicaranya, mari melihat uniform dalam aksi. Dalam kode berikut kita menggunakan u_time - jumlah detik sejak shader mulai berjalan - bersama dengan fungsi sinus untuk menganimasikan transisi jumlah warna merah di papan iklan.<div class="codeAndCanvas" data="time.frag"></div>
Seperti yang anda lihat, GLSL memilki banyak kejutan. GPU memiliki fungsi sudut akselerasi perangkat keras, trigonometri, dan eksponensial. Beberapa fungsi diantaranya adalah:
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() dan clamp().
Sekarang ini waktunya untuk bermain dengan kode di atas lagi.* Perlambat frekuensinya sampai warnanya berubah menjadi tak terlihat.
* Percepat sampai anda melihat warna tanpa berkedip.
* Bermainlah dengan tiga saluran (RGB) dalam frekuensi yang berbeda untuk mendapatkan pola dan perilaku yang menarik.
gl_FragCoord
Dengan cara yang sama GLSL memberi kita keluaran bawaan,
vec4 gl_FragColor, GLSL juga memberi kita masukan default, vec4 gl_FragCoord, yang menyimpan koordinat layar dari piksel atau fragmen layar tempat thread aktif berfungsi di. Dengan vec4 gl_FragCoord, kita tahu di mana sebuah thread bekerja di dalam billboard. Dalam hal ini kita tidak menyebutnya seragam karena akan berbeda dari utas ke utas, sebaliknya gl_FragCoord disebut bervariasi.<div class="codeAndCanvas" data="space.frag"></div>
Dalam kode di atas kita menormalisasi kordinat dari fragment dengan membaginya dengan total resolusi billboard. Dengan melakukan ini nilainya akan berada diantara
0.0 dan 0.1, yang memudahkan untuk memetakan nilai X dan Y ke saluran MERAH dan HIJAU.Di Shader, kita tidak memiliki terlalu banyak sumber daya untuk debugging selain memberikan warna yang kuat ke variabel dan mencoba memahaminya. Anda akan menemukan bahwa terkadang pengkodean dalam GLSL sangat mirip dengan meletakkan kapal di dalam botol. Sama-sama keras, indah, dan memuaskan.
Sekarang waktunya untuk mencoba dan menantang pemahaman kita terhadap kode ini.
* Bisakah anda mengatakan di mana kordinat
(0.0, 0.0) pada kanvas kita?* Bagaimana dengan
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) and (1.0, 1.0)? * Dapatkah Anda mengetahui cara menggunakan
u_mouse dengan mengetahui bahwa nilainya dalam piksel dan BUKAN nilai yang dinormalisasi? Bisakah Anda menggunakannya untuk memindahkan warna?* Dapatkah Anda membayangkan cara yang menarik untuk mengubah pola warna ini menggunakan koordinat
u_time dan u_mouse?Setelah melakukan latihan ini, Anda mungkin bertanya-tanya di mana lagi Anda bisa mencoba kekuatan shader baru Anda. Pada bab berikut, kita akan melihat cara membuat alat shader Anda sendiri di three.js, Processing, dan openFrameworks.
---
03/README It (03/README-it.md)
Uniforms
Finora abbiamo visto come la GPU gestisce un gran numero di thread paralleli, ciascuno responsabile nell'assegnazione d'un colore ad una frazione dell'immagine totale. Sebbene ogni thread parallelo è cieco nei confronti degli altri, dobbiamo essere in grado d'inviare alcuni input dalla CPU a tutti i thread. A causa dell'architettura della scheda grafica tali input saranno uguali (uniform) per tutti i thread e necessariamente impostati come di sola lettura. In altre parole, ogni thread riceve gli stessi dati che possono essere letti ma non possono essere cambiati.
Questi input sono chiamati
`uniform` e sono disponibili nella maggior parte di tipi supportati: `float`, `vec2`, `vec3`, `vec4`, `mat2`, `mat3`, `mat4`, `sampler2D` e `samplerCube`. Gli Uniforms sono definiti con i rispettivi tipi, all'inizio del codice, dopo aver definito la precisione della virgola mobile.glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // dimensione del Canvas (larghezza, altezza)
uniform vec2 u_mouse; // posizione del mouse (x,y) in pixels
uniform float u_time; // tempo in secondi da quando lo shader è iniziato
È possibile immaginare gli uniforms come piccoli ponti tra la CPU e la GPU. I nomi variano da applicazione ad applicazione, ma in questa serie di esempi userò:
`u_time` (tempo in secondi da quando lo shader è iniziato), `u_resolution` (la dimensione della finestra in cui lo shader è in corso d'elaborazione) e `u_mouse` (la posizione in pixel del mouse all'interno della finestra). Seguirò la convenzione di mettere `u_` prima del nome degli uniforms per essere espliciti sulla natura di questa variabile, ma incontrerete varie nomenclature per gli uniforms. Per esempio ShaderToy.com utilizza gli stessi uniforms, ma con i seguenti nomi:glsluniform vec3 iResolution; // dimensione del Canvas (in pixels)
uniform vec4 iMouse; // posizione del mouse in pixels. xy: corrente, zw: click
uniform float iTime; // tempo (in secondi) da quando lo shader è iniziato
Ma ora basta chiacchiere, vediamo gli uniforms in azione. Nel seguente codice utilizziamo
`u_time` - il numero di secondi da quando lo shader è iniziato - insieme ad una funzione seno per animare con una transizione la quantità di rosso sullo schermo.<div class="codeAndCanvas" data="time.frag"></div>
Come potete vedere GLSL ha molte sorprese. La GPU ha funzioni trigonometriche ed esponenziali, che sono accelerate dall'hardware. Alcune di queste funzioni sono:
`sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `pow()`, `exp()`, `log()`, `sqrt()`, `abs()`, `sign()`, `floor()`, `ceil()`, `fract()`, `mod()`, `min()`, `max()` and `clamp()`.Ora è il momento di giocare con il codice qui sopra.
* Rallentate la frequenza fino a quando il cambiamento di colore diventa quasi impercettibile.
* Aumentate la frequenza fino a vedere un solo colore, senza sfarfallio.
* Date tre frequenze differenti ai tre canali (RGB) per ottenere motivi e comportamenti interessanti.
gl_FragCoord
Allo stesso modo GLSL ci dà un output di default,
`vec4 gl_FragColor`, ma anche un input di default, `vec4 gl_FragCoord`, che contiene le coordinate sullo schermo del pixel o del screen fragment del thread attivo. Con `vec4 gl_FragCoord`, sappiamo dove un thread sta lavorando all'interno dello schermo. In questo caso la variabile non è un `uniform` perché sarà diversa da thread a thread. Le variabili che cambiano in ogni thread, come `gl_FragCoord`, sono chiamate varying.<div class="codeAndCanvas" data="space.frag"></div>
Nel codice qui sopra normalizziamo le coordinate del fragment dividendole per la risoluzione totale dello schermo. In questo modo i valori andranno tra
`0.0` e `1.0`, una tecnica che rende facile mappare i valori X e Y per i canali ROSSO e VERDE.Nel mondo degli shaders non abbiamo troppe risorse per il debug a parte l'assegnazione di colori intensi alle variabili e cercare di trovargli un senso. Scoprirete che a volte programmando in GLSL è come mettere una nave all'interno di una bottiglia: un processo difficile, bello e gratificante.
Ora è il momento di mettere in pratica gli insegnamenti che abbiamo imparato.
* Sapreste dire dove è la coordinata
`(0.0,0.0)` sul nostro schermo?* E dove sono
`(1.0,0.0)`, `(0.0,1.0)`, `(0.5,0.5)` e `(1.0,1.0)`?* Riuscite ad immaginare come usare
`u_mouse` sapendo che i valori sono espressi in pixel e NON in valori normalizzati? Sapresti usare `u_mouse` per muovere i colori?* Sapreste trovare un modo interessante per cambiare questo pattern grafico utilizzando
`u_time` e le coordinate `u_mouse`?Dopo aver fatto questi esercizi, ci si potrebbe chiedere dove si potrebbero provare i nuovi super poteri che gli shader ci hanno dato. Nel prossimo capitolo vedremo come creare i vostri shader in Three.js, Processing e openFrameworks.
---
03/README Jp (03/README-jp.md)
ユニフォーム変数
ここまで、GPUが沢山のスレッドを並列に処理する様子を見てきました。それぞれのスレッドは画像の各部分への色の割り当てを受け持っています。シェーダーではスレッド間の情報のやりとりを行うことはできませんが、CPUからそれぞれのスレッドに入力を送ることはできます。グラフィックカードは全てのスレッドにまったく同じ入力を、読み取り専用で送るように設計されています。それぞれのスレッドは同じデータを受け取り、それを書き換えることはできません。(訳注:英語の”uniform”には均一な、一様な、という意味があります)
これらの入力は
`uniform` 変数と呼ばれGLSLでサポートされているほとんどの型が使えます。サポートされている型には `float`、`vec2`、`vec3`、`vec4`、`mat2`、`mat3`、`mat4`、`sampler2D`、`samplerCube` などがあります。
uniform変数はシェーダーの冒頭、浮動小数点精度の設定の後に型指定付きで定義します。glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load
uniform変数はCPUとGPUの間の小さな架け橋だと考えることができます。変数の名前は実装次第で変わりますが、この本のサンプルでは一貫して
`u_time`(シェーダーが開始してから経過した秒数)、`u_resolution`(シェーダーが描画する領域の大きさ)、`u_mouse`(描画領域の中のマウスの位置)を渡すことにします。ここでは変数の種類を示すためにuniform変数の名前は `u_` で始めるという慣例に従っていますが、他の場所では異なる名前が使われているのも見かけることでしょう。
例えばShaderToy.comでは同じ意味を持つuniform変数に下記の名前が付けられています。
glsluniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iTime; // shader playback time (in seconds)
(訳注:uniform変数は開発者が自由に名前を決めることができ、上で挙げられている用途以外にも自由に使うことができます。C、JavascriptなどのCPUで走るプログラムからは、シェーダー側で決めた変数の名前を指定して値を渡すことができます)
説明はこれくらいにしてuniform変数を実際に使ってみましょう。下記のコードでは、
`u_time`(シェーダーが実行を始めてからの秒数)をサイン関数と組み合わせて使い、赤い画面の色を変化させています。<div class="codeAndCanvas" data="time.frag"></div>
GPUの驚くべき機能の1つには、角度や三角関数、指数関数などがハードウェア上で高速に処理されることが挙げられます。サポートされる関数には
`sin()`、 `cos()`、`tan()`、 `asin()`、`acos()`、 `atan()`、`pow()`、 `exp()`、`log()`、 `sqrt()`、`abs()`、 `sign()`、`floor()`、 `ceil()`、`fract()`、 `mod()`、`min()`、 `max()`、`clamp()` などがあります。さて、また上のコードで色々と実験をしてみましょう。
* 色が変わる頻度を下げて、変化にほとんど気がつかなくなるまで遅くしてみましょう。
* 変化のスピードを上げて、点滅がなくなりひとつの色に見えるまで速くしてみましょう。
* 3つのチャンネル(rgb)の変化の頻度を別々に変えて、面白いパターンを作ってみましょう。
gl_FragCoord
デフォルトの出力として
`vec4 gl_FragColor` を使うことができるのと同様に、GLSLにはデフォルトの入力として画面上の「フラグメント」、つまりピクセルの位置を表す `vec4 gl_FragCoord` が用意されています。`vec4 gl_FragCoord` を使うとスレッドが描画領域内のどこで作業をしているかを知ることができます。この `gl_FragCoord`はスレッドごとに異なる値を持っているためuniform変数とは呼ばず、代わりにvarying変数と呼びます。<div class="codeAndCanvas" data="space.frag"></div>
上のサンプルではフラグメントの座標を描画領域全体のサイズで割ることによって正規化しています。こうすると座標値の範囲が
`0.0` から `1.0` の間に収まるため、簡単にx座標とy座標の値をr(赤)とg(緑)のチャンネルに対応させることができます。シェーダーの世界ではデバッグに使える手段は限られています。判別しやすい色を変数の値に割り当てて確認するのは数少ない方法の一つです。GLSLのコーディングは時としてガラス瓶の中に船の模型を組み立てるのに似ています。どちらも同じくらい難しいですが、結果は美しく達成感があるものです。
それでは、サンプルが理解できているか確かめてみましょう。
* 座標
`(0.0,0.0)` が描画領域のどこを指すかわかりますか?*
`(1.0,0.0)`、`(0.0,1.0)`、`(0.5,0.5)`、`(1.0,1.0)` はどうでしょう?*
`u_mouse` の使い方はわかりますか? `u_mouse` の値は正規化されていないことに注意してください。この変数を使って色を変化させることはできますか?*
`u_time` と `u_mouse` の座標を使って色のパターンを自由に変化させてみましょう。さて、課題を終えたところで、このシェーダーの力を他にどこで使うことができるのか興味が湧いているのではないでしょうか。
次の章ではオリジナルのシェーダーをthree.js、Processing、openFrameworkで使う方法について説明します。
---
03/README Pl (03/README-pl.md)
Uniformy
Do tej pory widzieliśmy jak GPU zarządza wieloma równoległymi wątkami, z których każdy odpowiada za kolor części renderowanego obrazu. Choć wątki nie komunikują się między sobą, to jednak muszą jakoś otrzymywać input z CPU. Ze względu na architekturę karty graficznej taki input musi być jednakowy (ang. "uniform") dla wszystkich wątków i, z konieczności, tylko do odczytu (ang. "read-only"). Innymi słowy, każdy wątek otrzymuje takie same dane, które może odczytać, ale nie nadpisać, zmienić.
Inputy te nazywamy
uniformami i mogą być większości wspieranych typów: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D i samplerCube. Uniformy definiowane są zwykle na górze shaderu zaraz po przypisaniu domyślnej precyzji floatów.glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // wielkość/rozdzielczość kanwy (szerokość, wysokość)
uniform vec2 u_mouse; // pozycja myszy na kanwie (wyrażona w pikselach)
uniform float u_time; // czas w sekundach od załadowania shadera
Wyobraź sobie te uniformy jak małe mosty między CPU i GPU. Ich nazwy bywają różne, ale w tej książce używam:
u_time, u_resolution i u_mouse (przeczytaj komentarze w kodzie, aby wiedzieć, co robią). Podążam za konwencją dodawania u_ przed nazwą uniformów, aby było wiadomo, że nie są to zwykłe zmienne, ale ostatecznie jest to kwestia gustu. Przykładowo, ShaderToy.com używa takich samych uniformów, ale z następującym nazewnictwem: glsluniform vec3 iResolution; // rozdzielczość obszaru widoku (w pikselach)
uniform vec4 iMouse; // współrzędne piksela myszy: xy – bieżąca pozycja, zw – kliknięcie
uniform float iTime; // czas działania shadera (w sekundach)
(zwróć uwagę, że iResolution jest typu vec3, a iMouse typu vec4; uniformy te zawierają po prostu dodatkowe informacje, np.: stosunek szerokości do wysokości pikseli na ekranie, czy któryś z przycisków myszy został kliknięty albo czy jest przytrzymywany)Koniec gadania, czas zobaczyć uniformy w akcji. W poniższym kodzie używamy
u_time (liczby sekund od uruchomienia shadera) razem z funkcją sinus, aby stworzyć animację przejścia od koloru czerwonego do czarnego. <div class="codeAndCanvas" data="time.frag"></div>
Jak widać GLSL skrywa wiele niespodzianek, na przykład w postaci specjalnych, zaimplementowanych w hardwarze, funkcji trygonometryczne czy wykładniczych. Tutaj podaję część z nich:
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() i clamp().
Pobawmy się powyższym kodem:
* Zmniejsz częstotliwość tak bardzo, aby zmiany koloru stały się niezauważalne.
* Zwiększ częstotliwość do takiego stopnia, aby ujrzeć stały kolor bez migotania.
* Wstaw funkcje sinusoidalne o różnych częstotliwościach do pozostałych kanałów (zielonego i niebieskiego), aby uzyskać ciekawe efekty.
gl_FragCoord
GLSL daje nam nie tylko domyślny output
vec4 gl_FragColor, ale również domyślny input w postaci vec4 gl_FragCoord, który przechowuje współrzędne piksela (inaczej: fragmentu), nad którym aktualnie pracuje wątek - dzięki vec4 gl_FragCoord wiemy, gdzie wątek pracuje wewnątrz kanwy. Nie nazywamy go uniformem, ponieważ jego wartość różni się między wątkami. Zamiast tego gl_FragCoord nazywamy varying (z ang. "zmieniający się", "różniący się"). <div class="codeAndCanvas" data="space.frag"></div>
W powyższym kodzie, normalizujemy współrzędne fragmentu poprzez podzielenie go przez rozdzielczość kanwy. W ten sposób otrzymujemy wartości z przedziału od
0.0 do 1.0, co ułatwia zmapowanie współrzędnych x i y do, odpowiednio, czerwonego i zielonego kanału.W świecie shaderów nie mamy zbyt dużo sposóbów debuggowania poza przypisywaniem jaskrawych kolorów do zmiennych i wyciągania wniosków o działaniu shadera, na podstawie tego, co widzimy. Odkryjesz, że programowanie GLSL jest często jak wkładanie miniaturowych statków do butelki - jest to trudne, ale też piękne i satysfakcjonujące.
Czas przetestować nasze rozumienie kodu:
* Czy jesteś w stanie powiedzieć, gdzie na naszej kanwie znajduje się fragment o znormalizowanych współrzędnych
(0.0, 0.0)?* Co z fragmentami o znormalizowanych współrzędnych
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) i (1.0, 1.0)? * Czy jesteś w stanie użyć uniforma
u_mouse wiedząc, że wartości są nieznormalizowane? Spróbuj użyć go do manipulacji kolorem za pomocą ruchów myszki.* Czy jesteś sobie w stanie wyobrazić ciekawy sposób zmieniania koloru, łącząc współrzędne
u_mouse z u_time?Po wykonaniu tych ćwiczeń, zapewne zastanawiasz się, gdzie jeszcze można użyć twoich nowych shaderowych mocy. W następnym rozdziale zobaczymy, jak stworzyć swój własny shader w three.js, Processing i openFrameworks.
---
03/README Pt (03/README-pt.md)
Uniforms
Até agora, vimos como a GPU lida um grande número de threads em paralelo, cada uma sendo responsável por atribuir a cor a uma porção da imagem. Apesar de cada thread paralela não saber da existência das outras, precisamos ser capazes de enviá-las algumas entradas (inputs) da CPU. Devido à arquitetura das placas de vídeo, essas entradas serão iguais (uniform) para todas as threads e necessariamente determinadas como somente leitura. Em outras palavras, cada thread recebe os mesmos dados, os quais se podem ler mas não podem se alterar.
Essas entradas são chamamadas de
uniform e podem ser de tipos diferentes, como: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D e samplerCube. Uniformes são definidas com o tipo correspondente no começo do código, logo após atribuir a precisão padrão de pontos flutuantes,glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load
Imagine os uniforms como pequenas pontes entre a CPU e a GPU. Os nomes variam dependendo da implementação, mas nessa série de exemplos estarei sempre usando:
u_time (tempo em segundos desde que o shader foi iniciado), u_resolution (tamanho da tela onde o shader está sendo desenhado) and u_mouse (posição em pixels do mouse dentro da tela). Estarei seguindo a convenção ao colocar u_ antes do nome da uniform para evidenciar a natureza desta variável mas você encontrará outros tipos de nomenclatura para uniforms. Por exemplo ShaderToy.com utiliza os mesmos uniforms mas com os seguintes nomes:glsluniform vec3 iResolution; // viewport resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
uniform float iTime; // shader playback time (in seconds)
Chega de conversa, vamos ver os uniforms em ação. No código abaixo, usamos
u_time - o número de segundos desde que o shader começou a ser executado - junto com uma função de seno para animar a transição da quantidade de vermelho na tela.<div class="codeAndCanvas" data="time.frag"></div>
Como você pode ver, GLSL tem mais surpresas. A GPU tem funções angulares, trigonométricas e exponenciais aceleradas pelo hardware. Algumas dessas funções são:
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() e clamp().Agora é novamente a hora de experimentar com o código acima.
* Diminua a frequência até a mudança de cor se tornar quase imperceptível.
* Aumente a velocidade até que seja possível ver somente uma cor sem oscilações.
* Experimente alterar os valores dos três canais de cores (RGB) em diferentes frequências a fim de obter padrões e comportamentos interessantes.
gl_FragCoord
Da mesma maneira que GLSL nos dá um output padrão,
vec4 gl_FragColor, ele também nos dá um input padrão, vec4 gl_FragCoord, que possui as coordenadas de um pixel ou screen fragment com que a thread ativa está processando. Com vec4 gl_FragCoord podemos saber onde a thread está trabalhando dentro da tela. Neste caso, não chamaremos isso de uniform porque seu valor será diferente para cada thread, logo gl_FragCoord é chamada de varying.<div class="codeAndCanvas" data="space.frag"></div>
No código acima nós normalizamos as coordenadas do fragmento ao dividi-las pela resolução total da tela. Fazendo isso, os valores serão entre
0.0 e 1.0, o que facilita mapear os valores de X e Y para os canais RED e GREEN.No mundo dos shaders, não temos muitos recursos para depurar bugs além de atribuir uma cor marcante às variáveis e tentar entender o que está acontecendo com as mesmas. Você descobrirá que, às vezes, programar em GLSL é bem similar a construir navios dentro de garrafas. É igualmente difícil, bonito e gratificante.
Agora é a hora de tentar e desafiar a nossa compreensão desse código.
* Você pode dizer onde a coordenada
(0.0, 0.0) está em nossa tela?* E quanto
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) e (1.0, 1.0)?* Você pode adivinhar como se usa o uniform
u_mouse, sabendo que os valores são em pixel e NÃO em valores normalizados? Você pode usá-lo para mover as cores ao longo da tela?* Você consegue imaginar uma maneira interessante para mudar esse padrão de cor usando
u_time e as coordenadas de u_mouse?Após completar estes exercícios, você talvez se pergunte onde mais você pode aplicar seu novo superpoder de shader. No próximo capítulo veremos como fazer as nossas próprias ferramentas de shader em three.js, Processing, e openFrameworks.
---
03/README Ru (03/README-ru.md)
Uniform-переменные
В предыдущей главе мы увидели каким образом GPU управляет большим количеством параллельных потоков, каждый из которых отвечает за назначение цвета небольшой части изображения. Каждый из параллельных потоков не знает о состоянии остальных, но всё же нам бывает нужно передавать входные данные от CPU ко всем потокам одновременно. Из-за особенностей архитектуры графических карт, все такие входные данные будут одинаковыми для всех потоков (однородными, uniform) и доступными только для чтения. Другими словами, каждый поток принимает на вход одни и те же данные, которые он может прочитать и не может перезаписать.
Эти входы называются однородными (
uniform) и могут иметь практически любой из поддерживаемых типов: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D и samplerCube. Uniform-переменные с указанием своих типов объявляются вначале шейдера сразу после указания точности по умолчанию.glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Размер изображения (ширина, высота)
uniform vec2 u_mouse; // Положение курсора мыши в пикселях
uniform float u_time; // Время в секундах с момента загрузки
Uniform-переменные можно представлять себе как маленькие мостики между CPU и GPU. Имена могут изменяться от примера к примеру, но в этой серии примеров я всегда передаю следующее:
u_time (время в секундах с момента запуска шейдера), u_resolution (размер изображения) и u_mouse (положение мыши на изображении, выраженное в пикселях). Я буду писать u_ вначале имён uniform-переменных, чтобы их происхождение было обозначено явно, но на практике вы столкнётесь с самыми разными именами uniform-переменных. Например, ShaderToy.com объявляет эти же переменные со следующими именами:glsluniform vec3 iResolution; // разрешение области изображения (в пикселях)
uniform vec4 iMouse; // координаты мыши в пикселях. xy - текущие, zw - клик
uniform float iGlobalTime; // время работы шейдера (секунды)
Хватит слов, давайте посмотрим на юниформы в действии. В следующем коде мы используем
u_time (количество секунд с момента запуска шейдера) в комбинации с функцией синуса чтобы анимировать изменение количества красного цвета на экране.<div class="codeAndCanvas" data="time.frag"></div>
Как видите, GLSL содержит ещё много сюрпризов. GPU аппаратно ускоряет угловые, тригонометрические и экспоненциальные функции. Вот некоторые из них:
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() и clamp().Настало время поиграть с кодом выше.
* Уменьшите частоту так, чтобы изменение цвета стало почти незаметным.
* Увеличивайте её до тех пор, пока не увидите сплошной цвет без мерцания.
* Поиграйтесь с каналами RGB на различных частотах, чтобы добиться какого-нибудь интересного поведения.
gl_FragCoord
Подобно тому, как GLSL объявляет выходное значение
vec4 gl_FragColor по умолчанию, он так же предоставляет вход vec4 gl_FragCoord, содержащий координаты пикселя или фрагмента экрана, над которым работает данный поток. С помощью vec4 gl_FragCoord мы можем узнать где именно поток работает внутри изображения. В данном случае мы не называем входное значение однородным, потому что оно меняется от потока к потоку и называется изменяющимся (varying).<div class="codeAndCanvas" data="space.frag"></div>
В коде выше мы нормализуем координаты фрагмента, разделяя их на разрешение изображения. В результате значения переходят в диапазон между
0.0 и 1.0, что упрощает отображение значений X и Y на красный и зелёный каналы.В мире шейдеров у нас нет нормальных отладочных инструментов, поэтому приходится задавать переменным яркие цвета и пытаться извлечь из них смысл. Вы увидите, что иногда программирование на GLSL похоже на засовывание корабля в бутылку. Оно столь же сложно, сколь красиво и захватывающе.
Настало время проверить наше понимание приведённого выше кода.
* Укажите где находятся координаты
(0.0, 0.0) на изображении.* Как насчёт
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) и (1.0, 1.0)?* Догадайтесь как использовать
u_mouse, зная, что координаты даны в пикселях и НЕ нормализованы. Можете ли вы изменять цвета с помощью этой переменной?* Придумайте какой-нибудь интересный способ изменения цветов с помощью
u_time и u_mouse.После выполнения этих упражнения у вас скорее всего возникнет вопрос: где ещё можно применить мощь шейдеров? В следующей главе вы научитесь создавать шейдерные инструменты на three.js, Processing, и openFrameworks.
---
03/README Tr (03/README-tr.md)
Uniform'lar
Şimdiye kadar GPU'nun, her biri görüntünün toplamının bir kısmına renk atamaktan sorumlu olan çok sayıda paralel iş parçacığını nasıl yönettiğini gördük. Her paralel iş parçacığı diğerlerine kör olsa da, CPU'dan tüm iş parçacıklarına bazı girdiler gönderebilmemiz gerekir. Grafik kartının mimarisi nedeniyle bu girdiler tüm iş parçacıkları için eşit (uniform) olacak ve zorunlu olarak salt okunur (read only) olarak ayarlanacaktır. Başka bir deyişle, her iş parçacığı okuyabileceği ancak değiştiremeyeceği aynı verileri alır.
Bu girdilere
uniform denir ve desteklenen çoğu türde gelirler: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D ve samplerCube. Uniform'lar, varsayılan kayan nokta hassasiyetini atadıktan hemen sonra shader'ın en üstünde ilgili türle tanımlanır.glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Tuval boyutu (genişlik,yükseklik)
uniform vec2 u_mouse; // ekran piksellerinde fare konumu
uniform float u_time; // Yüklemeden bu yana saniye cinsinden süre
Uniform'ları CPU ve GPU arasındaki küçük köprüler gibi hayal edebilirsiniz. İsimler uygulamadan uygulamaya değişecektir ancak bu örnek serisinde her zaman şunları iletiyorum:
u_time (shader başladığından beri saniye cinsinden süre), u_resolution (shader'ın çizildiği pano boyutu) ve u_mouse (pano içindeki fare konumu piksel cinsinden). Bu değişkenin doğası hakkında açık olmak için uniform adından önce u_ koyma kuralını izliyorum, ancak her türlü uniform adı bulacaksınız. Örneğin ShaderToy.com aynı uniform'ları kullanır ancak şu isimlerle:glsluniform vec3 iResolution; // görüntü alanı çözünürlüğü (piksel cinsinden)
uniform vec4 iMouse; // fare piksel koordinatları. xy: geçerli, zw: tıklama
uniform float iTime; // shader oynatma süresi (saniye cinsinden)
Yeterince konuştuk, uniform'ları eylem halinde görelim. Aşağıdaki kodda, panodaki kırmızı miktarının geçişini canlandırmak için bir sinüs fonksiyonuyla birlikte
u_time'ı - shader çalışmaya başladığından beri geçen saniye sayısını - kullanıyoruz.<div class="codeAndCanvas" data="time.frag"></div>
Gördüğünüz gibi GLSL'in daha fazla sürprizi var. GPU, donanım hızlandırmalı açı, trigonometrik ve üstel fonksiyonlara sahiptir. Bu fonksiyonlardan bazıları şunlardır:
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() ve clamp().Şimdi yukarıdaki kodla oynama zamanı.
* Renk değişimi neredeyse algılanamaz hale gelene kadar frekansı yavaşlatın.
* Titreşmeden tek bir renk görene kadar hızlandırın.
* İlginç desenler ve davranışlar elde etmek için üç kanalla (RGB) farklı frekanslarda oynayın.
gl_FragCoord
GLSL'in bize varsayılan bir çıktı olan
vec4 gl_FragColor'ı vermesi gibi, bize varsayılan bir girdi olan vec4 gl_FragCoord'ı da verir; bu, aktif iş parçacığının üzerinde çalıştığı pikselin veya ekran parçasının ekran koordinatlarını tutar. vec4 gl_FragCoord ile bir iş parçacığının panonun neresinde çalıştığını biliriz. Bu durumda buna uniform demiyoruz çünkü iş parçacığından iş parçacığına farklı olacaktır, bunun yerine gl_FragCoord'a varying (değişen) denir.<div class="codeAndCanvas" data="space.frag"></div>
Yukarıdaki kodda, parçanın koordinatını panonun toplam çözünürlüğüne bölerek normalize ediyoruz. Bunu yaparak değerler
0.0 ve 1.0 arasına gelecektir, bu da X ve Y değerlerini KIRMIZI ve YEŞİL kanala eşlemeyi kolaylaştırır.Shader diyarında, değişkenlere güçlü renkler atamak ve onlardan bir anlam çıkarmaya çalışmak dışında hata ayıklama (debugging) için çok fazla kaynağımız yoktur. Bazen GLSL'de kodlamanın şişelerin içine gemi koymaya çok benzediğini keşfedeceksiniz. Aynı derecede zor, güzel ve tatmin edicidir.
Şimdi bu kod hakkındaki anlayışımızı deneme ve zorlama zamanı.
*
(0.0, 0.0) koordinatının tuvalimizin neresinde olduğunu söyleyebilir misiniz?* Peki ya
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) ve (1.0, 1.0)?* Değerlerin piksel cinsinden olduğunu ve normalize EDİLMEDİĞİNİ bilerek
u_mouse'u nasıl kullanacağınızı bulabilir misiniz? Renkleri hareket ettirmek için kullanabilir misiniz?*
u_time ve u_mouse koordinatlarını kullanarak bu renk desenini değiştirmenin ilginç bir yolunu hayal edebilir misiniz?Bu alıştırmaları yaptıktan sonra yeni shader güçlerinizi başka nerede deneyebileceğinizi merak edebilirsiniz. Sonraki bölümde three.js, Processing ve openFrameworks'te kendi shader araçlarınızı nasıl yapacağınızı göreceğiz.
---
03/README Ua (03/README-ua.md)
Uniforms
Раніше ми побачили, яким чином графічний процесор керує великою кількістю паралельних потоків, кожен з яких відповідає за призначення кольору частці загального зображення. Хоча кожен паралельний потік закритий для інших, ми повинні мати можливість надсилати деякі вхідні дані від CPU до всіх потоків. Через архітектуру відеокарти ці вхідні дані будуть однаковими (uniform - уніфікованими, однорідними) для всіх потоків і доступними лише для читання. Іншими словами, кожен потік отримує ті ж самі дані, які він може читати, але не може змінювати.
Ці вхідні дані називаються
uniform та мають більшість підтримуваних типів: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D і samplerCube . Uniform-змінні визначаються з відповідним типом у верхній частині шейдера відразу після встановлення точності для float-значень.glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Розмір полотна (ширина, висота)
uniform vec2 u_mouse; // Положення курсору на екрані
uniform float u_time; // Час у секундах з моменту запуску коду
Ви можете уявити uniform-змінні як маленькі містки між CPU та GPU. Назви для змінних можуть відрізнятися від реалізації до реалізації, але в цій серії прикладів я завжди передаю:
u_time (час у секундах із моменту запуску шейдера), u_resolution (розмір зображення де застосовується шейдер) і u_mouse (положення курсору всередині зображення, що вимірюється у пікселях). Я дотримуюся конвенції з префіксом u_ перед назвою uniform-змінних, щоб чітко означити природу таких змінних, але насправді ви можете зустріти різні назви. Наприклад, ShaderToy.com використовує ті ж самі змінні, але з наступними назвами:glsluniform vec3 iResolution; // роздільна здатність області зображення, у пікселях (viewport resolution)
uniform vec4 iMouse; // піксельні координати курсору. xy - поточні, zw - клік
uniform float iTime; // час роботи шейдера (у секундах)
Досить розмов, подивимося на уніформи в дії. У наведеному нижче коді ми використовуємо
u_time — кількість секунд із моменту запуску шейдера — разом із синус-функцією, щоб анімувати зміни у кількості червоного кольору на екрані.<div class="codeAndCanvas" data="time.frag"></div>
Як ви бачите, GLSL має ще багато сюрпризів. GPU має апаратне прискорення для кутових, тригонометричних та експоненціальних функцій. Ось деякі з цих функцій:
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() and clamp().Настав час знову пограти з наведеним вище кодом.
* Уповільнюйте частоту, щоб зміна кольору стала майже непомітною.
* Пришвидшуйте, поки не побачите один колір без мерехтіння.
* Пограйтеся із RGB-каналами та різними частотами для них, щоб отримати якусь цікаву поведінку.
gl_FragCoord
Подібно до того як GLSL, за замовчуванням, дає нам вихідні дані
vec4 gl_FragColor, він також дає нам вхідні дані за замовчуванням, vec4 gl_FragCoord, що містять екранні координати пікселя або фрагменту екрана, з якими працює активний потік. За допомогою vec4 gl_FragCoord ми знаємо, де саме працює поточний потік всередині зображення. У цьому випадку ми не називаємо його uniform, тому що він буде відрізнятися від потоку до потоку, натомість gl_FragCoord називається varying (змінливим).<div class="codeAndCanvas" data="space.frag"></div>
У наведеному вище коді ми нормалізуємо координати фрагмента, розділивши їх на роздільну здатність зображення. У результаті цього отримані значення будуть змінюватися у діапазоні від
0.0 до 1.0, що полегшує зіставлення X і Y значень з ЧЕРВОНИМ і ЗЕЛЕНИМ каналами.У світі шейдерів ми не маємо зручних інструментів для зневаджування програми, тому інколи доводиться призначати змінним якісь яскраві кольори, щоб за їх допомогою спробувати дістати потрібну інформацію. Ви виявите, що кодування на GLSL іноді дуже схоже на розміщення корабля у пляшку. Це однаково важко, красиво і захопливо.
Тепер настав час перевірити наше розуміння цього коду.
* Чи можете ви сказати, де знаходиться координата
(0.0, 0.0) на нашому полотні?* А як щодо
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) і (1.0, 1.0)?* Чи можете ви зрозуміти, як використати змінну
u_mouse, знаючи, що її значення вказані в пікселях і НЕ нормалізовані? Чи зможете за її допомогою і руху курсору змінювати кольори?* Чи можете ви вигадати цікавий спосіб для зміни кольорів за допомогою координат
u_time і u_mouse?Після виконання цих вправ ви можете поставити собі питання: де ще можна спробувати можливості шейдерів. У наступному розділі ми розглянемо, як створити власні шейдерні інструменти у three.js, Processing і openFrameworks.
---
03/README Vi (03/README-vi.md)
Uniform
Tới giờ ta đã biết cách mà GPU xử lý 1 số lượng lớn các thread song song, mỗi cái chịu trách nhiệm đổi màu cho 1 vùng trong cả màn hình. Dù mỗi thread không biết các thread khác đang làm gì, ta vẫn phải gửi dữ liệu từ CPU tới cho từng thread. Kiến trúc của GPU yêu cầu dữ liệu gửi cho các thread phải giống nhau (uniform) và không được thay đổi (read only).
Các dữ liệu đầu vào này được gọi là
uniform và hỗ trợ hầu hết các kiểu dữ liệu cơ bản như: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D và samplerCube. Uniform được định nghĩa cùng với kiểu dữ liệu tương ứng, ở phần trên cùng của code shader, ngay sau khi quy định độ chính xác của các số thực.glsl#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution; // Kích thước canvas (Rộng, cao)
uniform vec2 u_mouse; // Vị trí con trỏ chuột trong canvas
uniform float u_time; // Thời gian hiện tại tính từ lúc load xong shader
Bạn có thể hình dung uniform giống như các cầu nối giữa CPU và GPU. Tên của các biến có thể thay đổi theo từng chương trình nhưng trong quyển sách này tôi sẽ luôn dùng:
u_time (thời gian hiện tại tính từ lúc load xong shader), u_resolution (kích thước vùng mà shader sẽ vẽ) và u_mouse (vị trí con trỏ chuột trong vùng được vẽ). Tôi dùng quy tắc thêm tiền tố u_ vào trước các tên biến để đánh dấu các uniform. Ví dụ code trên ShaderToy.com cũng sử dụng quy tắc riêng:glsluniform vec3 iResolution; // Kích thước canvas
uniform vec4 iMouse; // Vị trí con trỏ chuột trong canvas (xy=vị trí hiện tại, zw=vị trí click)
uniform float iTime; // Thời gian hiện tại tính từ lúc load xong shader
Hãy xem thực tế uniform làm việc như thế nào. Ở đoạn code dưới đây tôi dùng
u_time - thời gian hiện tại tính bằng giây, kể từ lúc load xong shader - với hàm sine để kiểm soát sắc đỏ trong canvas theo thời gian.<div class="codeAndCanvas" data="time.frag"></div>
GLSL có nhiều điều thú vị. Phần cứng của GPU giúp tăng tốc các hàm lượng giác và luỹ thừa:
sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() và clamp(). Chúng rất nhanh.Cùng sửa đoạn code trên nào.
* Hãy giảm tần suất đổi màu xuống sao cho việc đổi màu khó có thể nhận ra được
* Hãy tăng tần suất đổi màu tới khi bạn chỉ nhìn thấy 1 màu duy nhất nhấp nháy liên tục
* Hãy thay đổi mỗi kênh RGB với một tần suất khác nhau, bạn sẽ thấy các kết quả rất bất ngờ
gl_FragCoord
Tương tự như biến lưu trữ giá trị output trong GLSL,
vec4 gl_FragColor, ta cũng có biến lưu trữ giá trị input, vec4 gl_FragCoord, là toạ độ của điểm ảnh (pixel) hoặc một vùng điểm ảnh (screen fragment) mà thread này đang xử lý. Ta biết rằng giá trị của vec4 gl_FragCoord khác nhau giữa từng thread, nên nó không phải là uniform.<div class="codeAndCanvas" data="space.frag"></div>
Ở đoạn code trên ta chuẩn hoá (normalize) toạ độ của từng fragment bằng cách chia nó cho kích thước của canvas. Bằng cách này, giá trị nhận được sẽ luôn nằm trong khoảng từ
0.0 tới 1.0, và sẽ khiến việc ánh xạ sang sắc độ RED và GREEN dễ hơn.Trong shader ta không có nhiều cách để debug lắm bên cạnh việc thử dùng một màu rất chói để kiểm tra. Code shader thi thoảng cũng giống như dựng một chiếc thuyền bên trong một cái chai, rất khó nhưng đẹp và khiến ta thoả mãn.
Giờ là lúc để thử xem ta hiểu code tới đâu.
* Bạn có biết toạ độ
(0.0, 0.0) nằm ở đâu trên canvas không?* Vậy còn
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5) và (1.0, 1.0)? Bạn có thể sử dụng
u_mouse để lấy giá trị điểm ảnh CHƯA chuẩn hoá (normalize)*? Bạn có thể dùng nó để thay đổi màu khi di chuyển con trỏ chuột?* Bạn có tưởng tượng được cách nào để tạo ra các mảng màu hay ho bằng cách sử dụng
u_time và u_mouse không?Sau khi làm các bài tập này, bạn có thể sẽ thắc mắc mình có thể sử dụng năng-lực-shader mới này của mình ở đâu nữa. Ở chương tới ta sẽ xem làm thế nào để tạo shader bằng three.js, Processing, và openFrameworks.
---
03/SUMMARY Pt (03/SUMMARY-pt.md)
Aprenda como usar variáveis Uniform. Variáveis uniform, ou simplesmente uniformes são as variáveis que carregam informação acessível igualmente de todas as threads do seu shader. O editor GSLS tem três uniformes setados para você.
glsluniform vec2 u_resolution; // Tamanho do canvas (largura,altura)
uniform vec2 u_mouse; // posição do mouse em pixels da tela
uniform float u_time; // Tempo em segundos desde carregamento
---
03/SUMMARY (03/SUMMARY.md)
Learn how to use Uniform variables. Uniform variables, or simply uniforms are the variables that carry information equally accessible from all of the threads of your shader. The GSLS editor has three uniforms set up for you.
glsluniform vec2 u_resolution; // Canvas size (width,height)
uniform vec2 u_mouse; // mouse position in screen pixels
uniform float u_time; // Time in seconds since load
---
04/README (04/README.md)
Running your shader
As part of the construction of this book and my art practice I made an ecosystem of tools to create, display, share and curate shaders. These tools work consistently across Linux, MacOS, Windows and Raspberry Pi and browsers without the need of changing your code.
Running your shaders on the browser
Display: all live examples in this book are displayed using glslCanvas which makes the process of running standalone shader incredibly easy.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
As you can see, it just needs a
canvas element with class="glslCanvas" and the url to your shader in the data-fragment-url. Learn more about it here.If you are like me, you will probably want to run shaders directly from the console, in that case you should check out glslViewer. This application allows you to incorporate shaders into your
bash scripts or unix pipelines and use it in a similar way to ImageMagick. Also glslViewer is a great way to compile shaders on your Raspberry Pi, which is the reason openFrame.io uses it to display shader artwork. Learn more about this application here.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Create: in order to illuminate the experience of coding shaders I made an online editor called glslEditor. This editor is embedded on the book's live examples, it brings a series of handy widgets to make more tangible the abstract experience of working with glsl code. You can also run it as a standalone web application from editor.thebookofshaders.com/. Learn more about it here.
If you prefer to work offline using SublimeText you can install this package for glslViewer. Learn more about it here.
Share: the online editor (editor.thebookofshaders.com/) can share your shaders! Both the embedded and standalone version have an export button where you can get an unique URL's to your shader. Also it has the ability to export directly to an openFrame.io.
Curate: Sharing your code is the beginning of you sharing your shader as artwork! Beside the option to export to openFrame.io I made a tool to curate your shaders into a gallery that can be embedded on any site, it’s name is glslGallery. Learn more here.
Running your shaders on your favorite framework
In case you already have experience programming in a framework like: Processing, Three.js, OpenFrameworks or SFML, you're probably excited to try shaders on these platforms you feel comfortable with. The following are examples of how to set shaders in some popular frameworks with the same uniforms that we are going to use throughout this book. (In the GitHub repository for this chapter, you'll find the full source code for these three frameworks.)
In Three.js
The brilliant and very humble Ricardo Cabello (aka MrDoob ) has been developing along with other contributors probably one of the most famous frameworks for WebGL, called Three.js. You will find a lot of examples, tutorials and books that teach you how to use this JavaScript library to make cool 3D graphics.
Below is an example of the HTML and JS you need to get started with shaders in three.js. Pay attention to the
id="fragmentShader" script, here is where you can copy the shaders you find in this book.text/ Detailed source-code truncated for AI context efficiency. /
In Processing
Started by Ben Fry and Casey Reas in 2001, Processing is an extraordinarily simple and powerful environment in which to take your first steps in code (it was for me at least). Andres Colubri has made important updates to the OpenGL and video in Processing, making it easier than ever to use and play with GLSL shaders in this friendly environment. Processing will search for the shader named
"shader.frag" in the data folder of the sketch. Be sure to copy the examples you find here into that folder and rename the file.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
In order for the shader to work on versions previous to 2.1, you need to add the following line at the beginning of your shader:
#define PROCESSING_COLOR_SHADER. So that it looks like this:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
For more information about shaders in Processing check out this tutorial.
In openFrameworks
Everybody has a place where they feel comfortable, in my case, that’s still the openFrameworks community. This C++ framework wraps around OpenGL and other open source C++ libraries. In many ways it's very similar to Processing, but with the obvious complications of dealing with C++ compilers. In the same way as Processing, openFrameworks will search for your shader files in the data folder, so don’t forget to copy the
.frag files you want to use and change the name when you load them.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
If you want to use the full set of uniforms contain on the specs of GlslViewer and GlslCanvas in a more simple way on OpenFrameworks I recommend using the ofxShader addon which will also have support for multiple buffers, material shaders, hotreload and automatic conversion for OpenGL ES in the Raspberry Pi. And your code will be as simple as doing
cpp//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
For more information about shaders in openFrameworks go to this excellent tutorial made by Joshua Noble.
---
04/README Ch (04/README-ch.md)
运行你的 shader
我制作了一套工具生态系统,用于创建、显示、分享与使用 shader,以此作为本书结构及我艺术实践的一部分。这些工具是跨平台的,无需更改代码就能在 Linux、MacOS、Windows、树莓派 和浏览器上表现一致。
在浏览器上运行你的 shader
显示: 本书中所有实例都可以用 glslCanvas 来显示,这样一来,运行独立的 shader 程序就变得非常简单.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
如你所见, 只需要创建一个类名为
class="glslCanvas" 的 canvas 元素,并将你的 shader 链接放在 data-fragment-url 中. 在 这里 可以了解更多.你可能会像我一样想要从命令行直接运行 shader,那你需要看看 glslViewer。这个应用程序可以将 shader 放到
bash 脚本或 unix 管道里,并且像 ImageMagick 一样使用它。此外,glslViewer 也是一个在 树莓派 上编译 shader 的好办法, 这就是 openFrame.io 用它来展示shader 作品的原因。在 这里 可以了解该应用程序的更多信息。bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -s 1 -o yourOutputImage.png
创建: 为了介绍 shader 编码的经验,我制作了在线编辑器 glslEditor。本书的实例中内嵌了这个编辑器。这个编辑器有很多好用的小组件,使编写 glsl 代码的体验更加直观。你也可以在 editor.thebookofshaders.com/ 上将其作为独立的 Web 应用运行。在 这里 了解更多。
如果你更喜欢用 SublimeText 离线编程,你可以安装 package for glslViewer。 在 这里 了解更多.
分享: 在线编辑器 (editor.thebookofshaders.com/) 可以分享你的 shader!内嵌版和独立版都有导出按钮,你可以通过这个按钮获得 shader 的唯一链接。编辑器也可以直接将 shader 导出到 openFrame.io。
使用: 分享代码只是分享 shader 作品的开始!除了导出到 openFrame.io,我还制作了使用 shader 的工具 glslGallery,它可以将 shader 放入画廊中,以便嵌入到任何网站. 在 这里 了解更多.
在你喜欢的框架上运行你的 shader
如果你使用过这些这些框架: Processing, Three.js, OpenFrameworks 或 SFML, 你可能更愿意在你觉得舒服的这些平台上编写 shader。下面将会介绍在这些框架中,用本书范式编写 shader 的方法。 (在 本章节的 GitHub 仓库中, 你能找到这三个框架的完整源码.)
Three.js
为人谦逊而非常有才华的 Ricardo Cabello (也就是 MrDoob )和许多贡献者 一起搭了可能是 WebGL 最知名的平台,Three.js。你可以找到无数程序示例,教程,书籍,教你如何用这个 JavaScript 库做出酷炫的 3D 图像。
下面是一个你需要的例子,教你用 three.js 玩转 shader。注意
`id="fragmentShader"`脚本,你要把下面的代码拷到里面。下面是一个 HTML 和 JS 的示例,
text/ Detailed source-code truncated for AI context efficiency. /
Processing
2001年由Ben Fry 和 Casey Reas 创建,Processing是一个极其简约而强大的环境,非常适合初尝代码的人(至少对于我来是这样)。关于 OpenGL 和视频,Andres Colubri为 Processing 平台做了很重要的更新,使得环境非常友好,玩 GLSL shader 比起以前大大容易了。Processing 会在你的 sketch 的
`data` 文件夹搜索名为 `"shader.frag"` 的文件。记得把这里的示例代码拷到你的文件夹里然后重命名 shader。processingPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
在 2.1 版之前的版本运行 shader,你需要在你的 shader 文件开头添加以下代码:
`#define PROCESSING_COLOR_SHADER`。所以它应该看起来是这样:
glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
更多 Processing 的 shader 教程戳 tutorial。
openFrameworks
每个人都有自己的舒适区,我的则是openFrameworks community。这个 C++ 框架打包了 OpenGL 和其他开源 C++ 库。在很多方面它和 Processing 非常像,但是明显和 C++ 编译器打交道一定比较麻烦。和 Processing 很像地,openFrameworks 会在你的 data 文件夹里寻找 shader 文件,所以不要忘记把你的后缀
`.frag` 的文件拷进去,加载的时候记得改名。cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
关于 shader 在 openFrameworks 的更多信息请参考这篇excellent tutorial,作者是 Joshua Noble。
---
04/README De (04/README-de.md)
Ausführung Deiner Shader
Beim Schreiben dieses Buches und im Rahmen meiner künstlerischen Tätigkeit habe ich eine Sammlung von Tools entwickelt, mit deren Hilfe man Shader programmieren, anzeigen, teilen und kuratieren kann. Dieses Tools laufen auf Linux Desktops, Rechnern mit MacOS, dem Raspberry Pi und auf Internet-Browsern. Sie sorgen dafür, dass Du Deine Shader dort nutzen kannst, ohne etwas an deren Programmcode verändern zu müssen.
Anzeige: Alle Live-Beispiele in diesem Buch werden mit glslCanvas angezeigt. Dieses Tool macht es unglaublich einfach, Shader ohne weitere Umstände im Internet-Browser auszuführen.
html<canvas class="glslCanvas" data-fragment-url="yourShader.frag" data-textures="yourInputImage.png" width="500" height="500"></canvas>
Wie Du oben siehst, benötigt man lediglich ein
`canvas` HTML-Element mit der Klassenzuweisung `class="glslCanvas"` und eine URL als Verweis auf Deine Shader-Datei im Attribut `data-fragment-url`. Mehr darüber kannst Du hier erfahren.Vielleicht geht es Dir wie mir, und Du möchtest Deine Shader direkt aus der Kommandozeile starten. In diesem Fall solltest Du Dir einmal den glslViewer anschauen. Diese Anwendung ermöglicht es Dir, Shader aus
`bash`-Skripten oder aus der Unix-Pipeline heraus zu starten, ganz ähnlich wie dies mit ImageMagick möglich ist. Außerdem bietet der glslViewer eine großartige Möglichkeit, Shader auf Deinem Raspberry Pi zu kompilieren. Das ist auch der Grund, warum das Projekt openFrame.io dieses Tool für die Präsentation von Computerkunst nutzt. Mehr über diese Anwendung kannst Du hier erfahren.bashglslViewer deinShader.frag deineEingabeGrafik.png -w 500 -h 500 -s 1 -o deineAusgabeGrafik.png
Entwickeln: Um die Entwicklung von Shadern im Internet-Browser zu ermöglichen, habe ich einen Online-Editor mit dem Namen glslEditor entwickelt. Dieser Editor kommt bei den Live-Beispielen in diesem Buch zum Einsatz. Er bringt eine Reihe praktischer Widgets mit, die Dich beim Experimentieren mit dem Shader-Code unterstützen. Du kannst diesen Editor auch als eigenständige Web-Anwendung über die URL editor.thebookofshaders.com/ ausführen lassen. Mehr über diesen Editor erfährst Du hier.
Wenn Du lieber offline statt online mit SublimeText arbeitest, kannst Du dieses Paket für den glslViewer installieren. Mehr darüber erfährst Du hier.
Teilen: Du kannst Deine Shader direkt aus dem Online-Editor (editor.thebookofshaders.com/) heraus mit anderen teilen. Sowohl die eingebettete als auch die eigenständige Variante des Editors verfügen über eine Export-Schaltfläche. Sie liefert Dir eine einzigartige URL als Referenz auf Deinen Shader. Diese URL kannst Du dann an andere weitergeben. Außerdem bietet der Editor die Möglichkeit, Deine Shader zur Veröffentlichung direkt an das Projekt openFrame.io zu senden.
Kuratieren: Deinen Shader-Code mit anderen zu teilen ist nur der Anfang. Du kannst Deine Shader auch als Werke der Computerkunst veröffentlichen. Jenseits der Weitergabe an das Projekt openFrame.io, habe ich ein Tool entwickelt, mit dem man Shader-Programme in eine Galerie innerhalb einer Webseite einbetten kann. Der Name dieses Tools lautet passenderweise glslGallery. Mehr darüber erfährst Du hier.
Ausführen von Shadern in Deiner bevorzugten Umgebung
Falls Du bereits Erfahrung mit der Programmierung in einer Umgebung wie Processing, three.js, OpenFrameworks oder SFML gesammelt hast, möchtest Du Deine Shader vielleicht in dieser Umgebung ausführen lassen. Die folgenden Codebeispiele zeigen Dir, wie man Shader unter Verwendung der gleichen Uniforms, die wir in diesem Buch verwenden, in diesen Umgebungen ausführen kann. (In der GitHub-Ablage dieses Kapitels findest Du den gesamten Sourcecode für die Einbindung von Shadern unter den drei genannten Umgebungen.)
Ausführung unter three.js
Der brillante und äußerst bescheidene Ricardo Cabello (aka MrDoob ) hat zusammen mit Gleichgesinnten eines der wahrscheinlich populärsten Frameworks für WebGL mit dem Namen three.js entwickelt. Du findest dort viele Beispiele, Tutorials und Bücher, die Dir zeigen, wie Du diese JavaScript-Bibliothek zur Erstellung cooler 3D-Grafiken nutzen kannst.
Hier folgt ein Beispiel für den HTML- und JS-Code, den Du für Deine ersten Experimente mit Shadern unter three.js benötigst. Bitte beachte das Script unter dem HTML-Tag
`id="fragmentShader"`. Dort kannst Du Deine Shader aus dem vorliegenden Buch einfügen.text/ Detailed source-code truncated for AI context efficiency. /
In Processing
Ursprünglich im Jahre 2001 von Ben Fry und Casey Reas entwickelt, verkörpert Processing eine unglaublich einfache und gleichzeitig leistungsfähige Umgebung für Deinen Einstieg in das Thema „Programmierung“. (Auch ich habe so angefangen.) Andres Colubri hat wichtige Erweiterungen für die Einbeziehung von openGL und Videofunktionen in Processing entwickelt. Sie machen es ganz einfach, in dieser Umgebung mit GLSL-Shadern zu arbeiten. Processing sucht dazu einfach nach einem Shader mit dem Namen
`shader.frag` in dem Unterverzeichnis `data` Deines Zeichenblocks („sketch“). Stelle einfach sicher, dass Du die Beispiele aus dem vorliegenden Buch unter diesem Dateinamen in dem genannten Verzeichnis ablegst.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Damit Dein Shader auch unter Versionen von Processing kleiner 2.1 läuft, musst Du lediglich die folgende Programmzeile an den Anfang Deines Shaders stellen:
`#define PROCESSING_COLOR_SHADER`. Das sieht dann so aus:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Mehr Informationen über den Einsatz von Shadern in Processing findest Du auch in diesem Tutorial.
In openFrameworks
Jeder hat einen Platz, an dem er oder sie sich besonders wohl fühlt. Bei mir ist das die openFrameworks Gemeinschaft. Diese C++-Umgebung ermöglicht die bequeme Einbindung von OpenGL und weiteren Open Source C++-Bibliotheken. In vielerlei Hinsicht ähnelt sie der Arbeit mit Processing, nur dass man es hier mit C++ und C++-Compilern zu tun hat. Genau wie Processing sucht openFrameworks nach Deinen Shader-Dateien im
`DATA`-Unterverzeichnis. Deshalb vergiss nicht, Deine `.frag`-Dateien dorthin zu kopieren und den Dateinamen entsprechend anzupassen, wenn Du diese Dateien ausführen willst.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Weitere Informationen über die Verwendung von Shadern in openFrameworks findest Du in diesem exzellenten Tutorial, das von Joshua Noble verfasst wurde.
---
04/README Es (04/README-es.md)
Ejecutando tu shader
En este punto seguro estás entusiasmado con poder probar shaders en las plataformas en las que te sientes cómodo. En los siguientes ejemplos veremos como agregarlos en algunos frameworks populares con las mismas uniforms con las que estamos trabajando en este libro. (En el repositorio de GitHub de este capítulo encontrarás el código completo de estos ejemplos.)
Nota 1: En caso de que no quieras utilizar los shaders en los siguientes frameworks pero quieras hacerlo fuera del navegador, puedes descargar y compilar glslViewer. Este programa corre en MacOS y en Raspberry Pi, permite ejecutar directamente los ejemplos desde la terminal.
Nota 2: Si no quieres usar WebGl con tus shaders y no te interesan los frameworks siguientes, puedes usar glslCanvas. Esta herramienta fue diseñada para este libro, pero se volvió tan útil que he terminado usándola en muchos proyectos.
En Three.js
El brillante y humilde Ricardo Cabello (también conocido como MrDoob) ha estado desarrollando junto con otros contribuidores probablemente el framework más conocido de WebGL, llamado Three.js. Encontrarás muchos ejemplos, libros y tutoriales para aprender a hacer cosas geniales en 3D con JavaScript.
Aqui abajo hay un ejemplo del HTML y JS necesario para poder empezar a utilizar shaders con three.js. Presta atención al script
`id="fragmentShader"`, aquí es donde puedes copiar los ejemplos de este libro.text/ Detailed source-code truncated for AI context efficiency. /
En Processing
Iniciado por Ben Fry y Casey Reas en 2001, Processing es un extraordinario y poderoso entorno en el que se puede aprender los primeros pasos con el código (al menos así fue para mí). Andrés Colubri ha hecho importantes cambios a la parte de OpenGL y video, logrando que sea más fácil y amigable utilizar shaders GLSL. Processing buscará el archivo
`"shader.frag"` en la carpeta `data` del sketch. En ese archivo debes poner los ejemplos que encuentres en este libro.processingPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Para hacer funcionar el shader en versiones previas a la 2.1, necesitas agregar la siguiente línea al comienzo de tu shader:
`#define PROCESSING_COLOR_SHADER`. Así es como quedaría:
glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Para más información sobre shaders en processing puedes chequear este tutorial.
En openFrameworks
Todos tienen un lugar en el que se sienten más cómodos, en mi caso, todavía sigue siendo openFrameworks. Este framework en C++ engloba openGL y otras librerías open source. En muchos puntos es muy parecido a Processing, pero con las complicaciones obvias de trabajar con los compiladores de C++. De la misma forma que Processing, openFrameworks buscará tus shaders en archivos de la carpeta data, no te olvides de copiar los archivos
`.frag` que quieres usar y cambiar el nombre cuando los cargas.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Para más información sobre shaders en openFrameworks ve a este excelente tutorial creado por Joshua Noble.
---
04/README Fr (04/README-fr.md)
Exécuter vos shaders
Pour les besoins de ce livre comme pour ma pratique artistique, j'ai créé un écosystème d'outils permettant de créer, d'afficher, de partager et d'organiser mes shaders.
Ces outils fonctionnent de la même manière sur Linux Desktop, MacOS, Raspberry Pi et dans les navigateurs sans avoir besoin d'altérer le code.
Affichage : tous les exemples de ce livre sont affichés dans la page grâce à glslCanvas qui facilite grandement la fabrication et l'affichage de shaders autonomes.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Comme vous pouvez le voir, il suffit de créer un élément
canvas auquel on applique la classe class="glslCanvas" et de lui passer l'url du fragment shader dans data-fragment-url.
Pour en savoir plus, vous pouvez lire ceci.Si vous êtes comme moi, vous aurez sans doute envie de lancer vos shaders en lignes de commandes, dans ce cas vous devriez jeter un oeil à glslViewer.
Cette application permet d'incorporer un shader dans un script
bash ou un pipeline Unix et de l'utiliser comme ImageMagick.glslViewer est aussi un bon moyen de compiler vos shaders sur un Raspberry Pi et c'est la raison pour laquelle openFrame.io l'utilise pour afficher les oeuvres.
Pour en savoir plus, cliquez ici.
bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Création : pour améliorer l'expérience de programmation des shaders, j'ai créé un éditeur disponible ici : glslEditor.
L'éditeur est embarqué dans les exemples de ce livre, il met à disposition une série de petits widgets qui rendent l'édition du code GLSL plus tangible et moins abstraite.
Vous pouvez également le lancer dans une fenêtre de navigateur à cette adresse editor.thebookofshaders.com.
Pour en savoir plus, cliquez ici.
Si vous préférez travailler avec SublimeText vous pouvez installer le package pour glslViewer.
Pour en savoir plus, cliquez ici.
Partager : l'éditeur en ligne (editor.thebookofshaders.com) vous permet de partager vos shaders !
La version autonome comme la version en ligne de l'éditeur permettent de sauver votre shader en ligne et d'obtenir une url unique.
Il est également possible d'exporter le shader pour qu'il fonctionne sur openFrame.io.
Organiser : pouvoir partager ses shaders est une bonne chose, en plus de l'export vers openFrame.io,
j'ai fait un outil permettant d'organiser vos shaders dans une galerie et d'intégrer cette galerie dans n'importe quel site.
Il s'appelle glslGallery, pour en savoir plus, cliquez ici.
Lancer vos shaders depuis votre plateforme favorite
Si vous avez déjà programmé à l'aide de frameworks/APIs comme : Processing, Three.js, OpenFrameworks ou SFML,
vous êtes sans doute impatients de tester vos shaders dans ces environnements.
Les exemples suivants montrent comment intégrer les shaders sur ces plateformes en conservant les conventions de nommage que nous utiliserons dans ce livre.
Vous retrouverez le code source complet sur le dépôt GitHub de ce chapitre.
Dans Three.js
L'humble et brilliant Ricardo Cabello (alias MrDoob) a développé avec d'autres contributeurs, l'un des frameworks WebGL les plus utilisés : Three.js.
Il existe de nombreuses ressources pour apprendre à se servir de ce framework JavaScript.
L'exemple ci-dessous, vous donne le code nécessaire pour utiliser un shader dans Three.js.
Notez bien la balise de script appelée
id="fragmentShader", c'est là qu'il faudra coller le code que vous trouverez dans ce livre.text/ Detailed source-code truncated for AI context efficiency. /
Dans Processing
Initié par Ben Fry et Casey Reas en 2001, Processing est un framework Java
extraordinairement simple et très puissant qui vous aidera à faire vos premiers pas dans le code créatif (ça a été mon cas).
Andres Colubri a contribué des mises à jour importantes concernant OpenGL et la gestion vidéo dans Processing.
Ces ajouts simplifient énormément l'intégration des shaders GLSL dans l'environnement de développement.
Processing va chercher le shader appelé
"shader.frag" dans le dossier data du sketch.
Assurez vous de copier les exemples du livre dans ce dossier et de les renommer correctement.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Pour que le shader fonctionne dans les versions antérieures à Processing 2.1, il faut ajouter la ligne suivante :
#define PROCESSING_COLOR_SHADER au début du shader.
Il devrait ressembler à cela :glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Pour en savoir plus sur les shaders dans Processing, vous pouvez vous reporter à ce tutoriel.
Dans openFrameworks
Chacun a sa zone de confort, pour moi, cela reste la communité openFrameworks.
Ce framework C++ intègre OpenGL et d'autres librairies C++ open source.
Il est très proche de Processing à ceci près que c'est un langage compilé et qu'il vaut donc mieux être habitué aux compilateurs C++.
Comme Processing, openFrameworks va chercher le fichier du shader dans le dossier
data, donc n'oubliez pas de créer un fichier .frag, d'y coller le contenu du shader et de spécifier le nom de ce fichier dans votre programme OF.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Pour plus d'informations sur les shader OpenFrameworks, vous pouvez vous reporter à cet excellent tutoriel écrit par Joshua Noble.
---
04/README Gr (04/README-gr.md)
Εκτελώντας τον shaders σας
Σαν μέρος της σύνθεσης αυτού του βιβλίου και της εξάσκησής μου στην πράξη, έφτιαξα ένα οικοσύστημα από εργαλεία ώστε που μπορεί κανείς να δημιουργήσει, να εμφανίσει, να μοιραστεί και να επιμεληθεί τους shaders του. Τα εργαλεία αυτά λειτουργούν ομοιόμορφα σε Linux, MacOS, Windows και Raspberry Pi και browsers (φυλλομετρητές) χωρίς να χρειάζεται να αλλάξετε τον κώδικά σας.
Εκτελώντας τους shaders σας σε browser
Εμφάνιση: όλα τα διαδραστικά παραδείγματα σε αυτό το βιβλίο απεικονίζονται χρησιμοποιώντας glslCanvas το οποίο κάνει τη διαδικασία του να τρέξουμε αυτόνομους shaders εξαιρετικά εύκολη.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Όπως βλέπετε, χρειάζεται μόνο ένα
canvas (HTML) element με class="glslCanvas" και το url του shader στο data-fragment-url. Μάθετε περισσότερα εδώ.Αν είστε σαν εμένα, πιθανόν θα θέλετε να τρέξετε shaders απευθείας από τη γραμμή εντολών, σε αυτή την περίπτωση προτείνω να δείτε το glslViewer. Αυτή η εφαρμογή σας επιτρέπει να ενσωματώσετε shaders στα scripts (σενάρια εκτέλεσης) σας φλοιού
bash ή σε pipelines ("σωληνώσεις" εργαλείων) unix και να τα χρησιμοποιήσετε παρόμοια με το ImageMagick. Επίσης, το glslViewer είναι ένας πολύ καλός τρόπος να μεταγλωττίζετε shaders στο Raspberry Pi σας, ο οποίος είναι και ο λόγος που το openFrame.io το χρησιμοποιεί για να εμφανίσει έργα τέχνης με shaders. Βρείτε περισσότερα για αυτή την εφαρμογή εδώ.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Δημιουργία: προκειμένου να αναδείξω την εμπειρία του να γράφουμε shaders, έφτιαξα ένα online editor (πρόγραμμα επεξεργασίας κειμένου) με όνομα glslEditor. Αυτός ο editor είναι ενσωματωμένος στα διαδραστικά παραδείγματα του βιβλίου. Μας δίνει ένα σύνολο από χρήσιμα widgets (εργαλεία διεπαφής) ώστε να κάνει πιο "απτή" την αφηρημένη εμπειρία του να δουλεύουμε με κώδικα glsl. Μπορείτε επίσης να τον τρέξετε σαν αυτόνομη εφαρμογή web από εδώ editor.thebookofshaders.com/. Μάθετε περισσότερα σχετικά εδώ.
Αν προτιμάτε να δουλεύετε offline με το SublimeText μπορείτε να εγκαταστήσετε αυτό το πακέτο για τον glslViewer. Μάθετε περισσότερα εδώ.
Διανομή: με τον online editor (editor.thebookofshaders.com/) μπορείτε να μοιραστείτε τους shaders σας! Και η online και ενσωματωμένη και η ανεξάρτητη έκδοση έχουν ένα κουμπί export (εξαγωγή) με το οποίο μπορείτε να πάρετε ένα μοναδικό URL για τον shader σας. Επίσης έχει τη δυνατότητα να κάνει export απευθείας σε ένα openFrame.io.
Επιμέλεια: Το να μοιραστείτε τον κώδικά σας είναι το πρώτο βήμα προς το να μοιραστείτε τον shader σας σαν έργο τέχνης! Εκτός από την επιλογή να κάνετε export σε openFrame.io έφτιαξα ένα εργαλείο με το οποίο μπορείτε να οργανώσετε τους shaders σας σε μια έκθεση που μπορεί να ενσωματωθεί σε οποιοδήποτε site (ιστοχώρο), το όνομά του είναι glslGallery. Δείτε περισσότερα εδώ.
Εκτελώντας τους shaders στο περιβάλλον της προτίμησής σας
Αν έχετε ήδη εμπειρία προγραμματισμού σε κάποιο περιβάλλον όπως: Processing, Three.js, OpenFrameworks ή SFML, θα ανυπομονείτε πιθανόν να δοκιμάσετε shaders στις πλατφόρμες με τις οποίες αισθάνεστε οικεία. Τα παρακάτω είναι παραδείγματα για το πως να ορίσετε shaders σε κάποια γνωστά περιβάλλοντα με τα ίδια uniforms τα οποία θα χρησιμοποιήσουμε σε όλο το βιβλίο. (Στο GitHub repository αυτού του κεφαλαίου, θα βρείτε ολόκληρο τον κώδικα γι' αυτά τα τρία περιβάλλοντα.)
Σε Three.js
Ο ευφυέστατος και πολύ σεμνός Ricardo Cabello (επίσης γνωστός και σαν MrDoob ) αναπτύσσει ανάμεσα σε άλλους συνεργάτες ένα πιθανόν από τα πιο γνωστά περιβάλλοντα για WebGL, που ονομάζεται Three.js. Θα βρείτε πολλά παραδείγματα, μαθήματα και βιβλία που διδάσκουν πως να χρησιμοποιήσετε αυτή τη βιβλιοθήκη JavaScript για να δημιουργήσετε φοβερά 3D γραφικά.
Ακολουθεί ένα παράδειγμα για τις HTML και JS που που χρειάζεστε για να ξεκινήσετε με shaders σε three.js. Δώστε ιδιαίτερη σημασία στο script
id="fragmentShader", εδώ είναι που μπορείτε να αντιγράψετε τους shaders που βρίσκετε σε αυτό το βοβλίο.text/ Detailed source-code truncated for AI context efficiency. /
Σε Processing
Η Processing η οποία πρωτοξεκίνησε από τους Ben Fry και Casey Reas το 2001, είναι ένα εξαιρετικά απλό και ισχυρό περιβάλλον για να κάνετε τα πρώτα σας βήματα σε κώδικα (σίγουρα ήταν για 'μένα). Ο Andres Colubri έχει κάνει σπουδαίες ενημερώσεις για την OpenGL και το video στην Processing, καθιστώντας το εκολότερο από ποτέ να παίξει κανείς με GLSL shaders στο φιλικό αυτό περιβάλλον. Η Processing ψάχνει για τον shader με όνομα
"shader.frag" στον φάκελο data του sketch ("σκίτσο" - το project της Processing). Αντιγράψτε τα παραδείγματα που βρίσκετε εδώ σε αυτό τον φάκελο, και μετονομάστε το αρχείο.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Προκειμένου να δουλέψει ο shader σε εκδόσεις παλιότερες της 2.1, πρέπει να προσθέσετε την παρακάτω γραμμή στην αρχή του shader:
#define PROCESSING_COLOR_SHADER, έτσι ώστε να δείχνει ως εξής:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Για περισσότερες πληροφορίες σχετικά με τους shaders σε Processing, δείτε αυτό το μάθημα.
Σε openFrameworks
Για τον καθένα υπάρχει ένα μέρος όπου νιώθει άνετα, στην περίπτωσή μου, εξακολουθεί να είναι η κοινότητα openFrameworks. Αυτή η βιβλιοθήκη C++ παρέχει ένα πλαίσιο αφαίρεσης γύρω απο την OpenGL και άλλες βιβλιοθήκες C++ ανοιχτού κώδικα. Κατά πολλές έννοιες είναι παρόμοια με την Processing, αλλά με τις προφανείς επιπλοκές του να δουλεύει κανείς με μεταγλωττιστές C++. Όμοια με την Processing, η openFrameworks αναζητά τα αρχεία shader στο φάκελο data, οπότε μην παραλείψετε να αντιγράψετε τα αρχεία
.frag που θέλετε να χρησιμοποιήσετε και να αλλάξετε το όνομά τους όταν τα φορτώνετε.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Αν θέλετε να χρησιμοποιήσετε το πλήρες σύνολο απο uniforms που υπάρχουν στον ορισμό των GlslViewer και GlslCanvas με απλούστερο τρόπο σε OpenFrameworks, συνιστώ το addon (πρόσθετο) ofxShader το οποίο επίσης υποστηρίζει πολλαπλούς buffers (μνήμη εικόνας), shaders υλικών, hotreload (άμεση επανεκτέλεση) και αυτόματη μετατροπή σε OpenGL ES για το Raspberry Pi. Και ο κώδικάς σας θα απαιτεί μόνο το παρακάτω:
cpp//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
Για περισσότερες πληροφορίες για τους shaders σε openFrameworks δείτε αυτό το εξαιρετικό μάθημα από τον Joshua Noble.
---
04/README Id (04/README-id.md)
Menjalankan Shader
Sebagai bagian dari konstruksi buku ini dan latihan seniku, aku membuat ekosistem alat untuk membuat, menampilkan, berbagi, dan mengkurasi shader. Alat ini bekerja secara konsisten di Linux, MacOS, Windows dan Raspberry Pi dan browser tanpa harus mengubah kodenya.
Menjalankan Shader pada Browser
Tampilan: seluruh contoh langsung dalam buku ini ditampilkan menggunakan glslCanvas yang membuat proses menjalankan shader mandiri sangat mudah.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Seperti yang dapat anda lihat, hanya membutuhkan elemen kanvas dengan class="glslCanvas" dan url shader anda dalam data-fragment-url. Pelajari lebih lanjut tentang itu di sini.Jika anda seperti saya, anda mungkin akan menjalankan shader langsung dari kosol, dalam kasus tersebut anda harus melihat glslViewer. Aplikasi ini memperbolehkan anda untuk menggabungkan shader ke skrip
bash atau pipeline unix dan menggunakannya sama mirip ImageMagick. glslViewer juga merupakan cara yang bagus untuk menkompilasi shader pada Raspberry Pi, yang mana merupakan alasan openFrame.io menggunakan itu untuk menampilkan artwork shader. Pelajari lebih lanjut tentang aplikasi ini di sinibashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Membuat: untuk menerangi pengalaman pengkodean shader, saya membuat editor online bernama glslEditor. Editor ini disematkan pada contoh langsung buku, ini membawa serangkaian widget praktis untuk membuat pengalaman abstrak bekerja dengan kode glsl lebih nyata. Anda juga dapat menjalankannya sebagai aplikasi web mandiri dari editor.thebookofshaders.com/. Pelajari lebih lanjut tentang itu di sini.
Jika anda memilih untuk mengerjakannya offline menggunakan SublimeText anda dapat memasang paket untuk glslViewer. Pelajari lebih lanjut di sini.
Bagikan: online editor (editor.thebookofshaders.com/) dapat membagikan shader anda! Baik versi yang disematkan maupun yang berdiri sendiri memiliki tombol ekspor tempat Anda bisa mendapatkan URL unik untuk shader Anda. Juga memiliki kemampuan untuk mengekspor langsung ke openFrame.io.
Kurasi: Berbagi kode Anda adalah awal dari Anda membagikan shader sebagai karya seni! Selain opsi untuk mengekspor ke openFrame.io, saya membuat alat untuk mengkurasi shader Anda ke galeri yang dapat disematkan di situs mana pun, namanya glslGallery. Pelajari lebih lanjut di sini.
Menjalankan Shader pada Framework Favoritmu
Dalam kasus kamu mempunyai pengalaman dalam pemrograman pada framework seperti: Processing, Three.js, OpenFrameworks or SFML, anda mungkin tertarik untuk mencoba shader pada platform yang nyaman bagi anda. Contoh berikut adalah cara untuk menyetel shader pada beberapa framework terkenal denggan uniform yang sama yag akan kita gunakan di seluruh buku ini, (Dalan Repositori Bithub Bab ini, anda akan menemukan kode sumber lengkap untuk 3 framework ini).
Dan Three.js
Ricardo Cabello yang brilian dan sangat rendah hati (alias MrDoob) telah berkembang bersama dengan kontributor lainnya mungkin salah satu kerangka kerja paling terkenal untuk WebGL, yang disebut Three.js. Anda akan menemukan banyak contoh, tutorial dan buku yang mengajarkan Anda bagaimana menggunakan perpustakaan JavaScript ini untuk membuat grafik 3D yang keren.
Di bawah ini adalah contoh HTML dan JS yang Anda butuhkan untuk memulai shader di three.js. Perhatikan skrip
id="fragmentShader, di sinilah Anda dapat menyalin shader yang Anda temukan di buku ini.text/ Detailed source-code truncated for AI context efficiency. /
Dalam Processing
Dimulai oleh Ben Fry dan Casey Reas pada 2001, Processing adalah lingkungan yang sangat sederhana dan kuat untuk mengambil langkah pertama Anda dalam kode (setidaknya untuk saya). Andres Colubri telah membuat pembaruan penting pada openGL dan video dalam Processing, membuatnya lebih mudah dari sebelumnya untuk menggunakan dan bermain dengan GLSL shader dalam lingkungan yang ramah ini. Pemrosesan akan mencari shader bernama
"shader.frag" di folder data sketsa. Pastikan untuk menyalin contoh yang Anda temukan di sini ke dalam folder itu dan ganti nama file. cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Agar shader bekerja pada versi sebelum 2.1, Anda perlu menambahkan baris berikut di awal shader Anda:
#define PROCESSING_COLOR_SHADER. Sehingga terlihat seperti ini:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Untuk informasi lebih lanjut mengenai shader dalam Processing lihatlah tutorial ini.
Dalam openFrameworks
Setiap orang memiliki tempat yang mereka rasa nyaman, dalam kasus saya, itu masih komunitas openFrameworks. Framework C++ ini membungkus OpenGL dan library C++ open source lainnya. Dalam banyak hal, ini sangat mirip dengan Pemrosesan, tetapi dengan komplikasi yang jelas saat berurusan dengan kompiler C++. Dengan cara yang sama seperti Processing, openFrameworks akan mencari file shader Anda di folder data, jadi jangan lupa untuk menyalin file
.frag yang ingin Anda gunakan dan ubah namanya saat Anda memuatnya.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Jika Anda ingin menggunakan set lengkap seragam yang berisi spesifikasi GlslViewer dan GlslCanvas dengan cara yang lebih sederhana di OpenFrameworks, saya sarankan menggunakan addon ofxShader yang juga akan memiliki dukungan untuk beberapa buffer, material shader, hotreload, dan konversi otomatis untuk OpenGL ES di Raspberry Pi. Dan kode Anda akan sesederhana melakukannya
cpp//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
Untuk informasi lebih lanjut mengenai shader dalam openFrameworks lihatlah tutorial bagus ini.
---
04/README It (04/README-it.md)
Eseguite il vostro shader
Nell'ambito della realizzazione di questo libro e della mia pratica artistica ho creato un ecosistema di strumenti per creare, visualizzare, condividere e curare gli shaders. Questo strumento funziona in modo coerente su Linux Desktop, MacOS, Raspberry Pi e browser, senza la necessità di dover cambiare il vostro codice.
Visualizzare: tutti gli esempi di questo libro vengono visualizzati utilizzando glslCanvas che rende il processo di esecuzione dello shader standalone incredibilmente facile.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Come potete vedere, è solo necessario l'elemento
`canvas` con l'attributo `class="glslCanvas"` e l'indirizzo verso il vostro shader nel `data-fragment-url`. Scoprite di più a proposito cliccando qui.Se siete come me, probabilmente vorrete eseguire gli shader direttamente dalla console. In questo caso date un'occhiata a glslViewer. Questa applicazione consente d'incorporare gli shader nel vostro script
`bash` o pipelines UNIX e d?utilizzarli in modo simile a ImageMagick. Anche glslViewer è un ottimo modo per compilare gli shader sul Raspberry Pi, motivo per il quale openFrame.io lo usa per visualizzare le opere d'arte di tipo shader. Potete trovare ulteriori informazioni su questa applicazione cliccando qui.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Creare: per migliorare la programmazione degli shader, ho realizzato un editor online chiamato glslEditor. Questo editor è integrato agli esempi del libro e mette a disposizione una serie di comodi widget per rendere più tangibile il codice astratto GLSL. È anche possibile eseguirlo come un'applicazione standalone web da editor.thebookofshaders.com/. Scoprite di più a proposito cliccando qui.
Se si preferisce lavorare offline utilizzando SublimeText è possibile installare questo pacchetto per glslViewer. Scoprite di più cliccando qui.
Condividere: l'editor online (editor.thebookofshaders.com/) può condividere i tuoi shader! Sia la versione standalone che quella integrata hanno un pulsante per esportare con il quale è possibile ottenere un URL unico verso il vostro shader. Avete anche la possibilità d'esportare direttamente verso un openFrame.io.
Curare: Condividere il vostro codice è la prima tappa per intendere il vostro shader come un'opera d'arte! Accanto alla possibilità di esportare verso openFrame.io ho fatto uno strumento per curare i vostri shader in una galleria che può essere integrata su qualsiasi sito, il suo nome è glslGallery. Per saperne di più cliccando qui.
Eseguire i vostri shader nel vostro framework preferito
Nel caso in cui si dispone già di esperienze di programmazione in framework quali: Processing, Three.js, OpenFrameworks o SFML, probabilmente sarete ansiosi di provare gli shader sulle piattaforme su cui vi trovate bene. I seguenti sono esempi di come impostare gli shader in alcuni dei framework più popolari con le stesse uniforms che andremo ad utilizzare in questo libro. (Nella repository GitHub per questo capitolo, troverete il codice sorgente completo per questi tre framework.)
In Three.js
Il brillante e molto umile Ricardo Cabello (aka MrDoob) ha sviluppato insieme ad altri collaboratori, probabilmente uno dei più famosi framework per WebGL, chiamato Three.js. Troverete un sacco di esempi, tutorial e libri che vi insegneranno come utilizzare questa libreria JavaScript per fare grafica 3D.
Di seguito è riportato un esempio di codice HTML e JS per iniziare con gli shader in three.js. Prestate attenzione allo script
`id="fragmentShader"`, qui è dove è possibile copiare gli shader che si trovano in questo libro.text/ Detailed source-code truncated for AI context efficiency. /
In Processing
Iniziato da Ben Fry e Casey Reas nel 2001, Processing è un framework straordinariamente semplice e potente in cui muovere i primi passi nel codice (o almeno lo è stato per me). Andres Colubri ha fatto importanti aggiornamenti a openGL e la gestione video in Processing, rendendo più facile che mai usare e giocare con i GLSL shader. Processing cercherà lo shader chiamato
`"shader.frag"` nella cartella `data` dello sketch. Assicuratevi di copiare gli esempi che trovate qui in quella cartella e rinominate il file.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Affinché lo shader lavori su versioni precedenti alla 2.1, è necessario aggiungere la seguente riga all'inizio del vostro Shader:
`#define PROCESSING_COLOR_SHADER`. In questo modo dovrebbe assomigliare a:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Per ulteriori informazioni sugli shader in Processing controllate questo tutorial.
In openFrameworks
Ognuno ha un luogo in cui sentirsi a proprio agio e, nel mio caso, è ancora la comunità di openFrameworks. Questo framework C++ integra OpenGL e altre librerie C++ open source. Per molti aspetti è molto simile a Processing, ma con le ovvie complicazioni dovute ai compilatori C++. Allo stesso modo di Processing, openFrameworks cercherà i tuoi file shader nella cartella dati, quindi non dimenticate di copiare i file
`.frag` che si desiderano utilizzare e modificate il nome quando li si carica.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Per ulteriori informazioni sugli shader in openFrameworks consultate questo ottimo tutorial fatto da Joshua Noble.
---
04/README Kr (04/README-kr.md)
Running your shader
이 시점에서, 슬슬 익숙하던 플랫폼에 쉐이딩 기술을 써보고 싶을 것이다. 아래는 사람들이 가장 많이 쓰는 플랫폼들에서 쉐이더를, 또 전 챕터에서 본 uniform 형식을 그대로 쓸수 있는 부분이다. (In the GitHub repository for this chapter, 이 세개의 플랫폼에 대한 소스코드는 여기서 확인할수 있다.)
Note 1: 만약 아래의 프레임워크 외에 것에서 구동하고 싶다면, glslViewer를 다운받아서 컴파일할수 있다. 터미널에서 구동되므로, MacOS와 RasberryPi등에서도 구동되며, 이책의 예제들은 모두 최적화 되어 있다.
Note 2: 만약 WebGL에서 쉐이더를 구동하고, 다른 프레임워크를 따로 쓰고 싶지 않다면, glslCanvas를 이용해서 할수 있다. 이 웹 툴은 이 책에 최적화 되어 있고, 실제로 저자가 프로젝트마다 사용하는 툴이기도 하다.
Three.js 에서
Ricardo Cabello (aka MrDoob ) 가 다른 참여자참여자들과 개발한 WebGL을 이용한 프레임 워크인 Three.js. 많은 예제와, 튜토리얼, 책들이 존재하고, 이를 이용해 여러 3D graphics데모를 만들어 볼수 있다.
아래는 HTML과 JS를 이용해 three.js를 구동하는 예제이다.
`id="fragmentShader"`부분을 보면, 쉐이더가 어디에서 적용되는지 볼수 있다.text/ Detailed source-code truncated for AI context efficiency. /
Processing에서
Ben Fry 와 Casey Reas 에 의해 2001년에 시작되었고, Processing 는 간단하고 강력한 환경을 제공하고, 초보자들에게 사랑받는 툴이다. (적어도 저자는 그렇게 생각한다고 한다) Andres Colubri 는 프로세싱에서, openGL부분과 비디오 프로세싱부분에 중요한 업데이트를 했고, 이것으로 인해 GLSL 쉐이더를 구동시키기가 한결 편해졌다. 프로세싱은
`"shader.frag"`라는 파일을 `data`폴더에서 찾는다. 책에서 예제를 구동할거라면, 이 폴더에 파일을 rename하여 저장하고 사용하면 된다.processingPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
쉐이더가 2.1 전버젼부터 작동되려면,
`#define PROCESSING_COLOR_SHADER` 를 쉐이더 제일 위편에 넣어야 작동될것이다. 아래와 같이:
glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
더 자세한 사항은 여기서 살펴보기 바란다. tutorial
openFrameworks 에서
각자 가장 편한 구동 프레임워크가 있을것인데, 저자의 경우는 openFrameworks community이다. C++로 이루어진 OpenGL와 유용한 C++라이브러리들을 wrap한 프레임 워크이고, Processing과 제법 흡사하다. Processing과 마찬가지로, openFrameworks도 data폴더에서 shader파일을 서치한다.
`.frag` 확장자명을 적는것 또한 까먹지 말자. cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
더 자세한 정보는 Joshua Noble가 만든 강좌를 보기 바란다.
---
04/README Pl (04/README-pl.md)
Korzystanie z shaderów
W ramach prac nad tą książką stworzyłem ekosystem narzędzi do tworzenia, wyświetlania, udostępniania i organizowania shaderów. Narzędzia te działają, bez konieczności dostosowywania kodu, na systemach Linux, MacOS, Windows i Raspberry Pi oraz w przeglądarce.
Korzystanie z shaderów w przeglądarce
Wyświetlaj: wszystkie interaktywne przykłady w tej książce wyświetlane są dzięki glslCanvas, który znacząco ułatwia proces korzystania z shaderów.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Jak widać,
glslCanvas wymaga jedynie elementu canvas z class="glslCanvas" oraz URL do twojego shadera w data-fragment-url. Aby dowiedzieć się więcej, kliknij tutaj.Jeśli podobnie jak ja chciałbyś móć korzysatć z shaderów w konsoli, to glslViewer powinien cie zainteresować. Aplikacja ta pozwala zintegrować shadery z
bashowymi skryptami i Unixowymi pipeline'ami, podobnie jak ImageMagick. Ponadto, glslViewer pozwala ci kompilować shadery na Raspberry Pi - właśnie dlatego openFrame.io używa go do wyświetalania swoich shaderowych dział sztuki. Kliknij tutaj, aby dowiedzieć się więcej.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Twórz: w celu usprawnienia procesu pisania shaderów stworzyłem edytor online glslEditor. Edytor ten jest wykorzystywany w interaktywnych przykładach tej książki. Dodaje on poręczne widżety, czyniące abstrakcyjne doświadczenie pracy z kodem GLSL bardziej uchwytnym. Możesz również korzystać z niego jako samodzielnej aplikacji webowej pod adresem editor.thebookofshaders.com/. Kliknij tutaj, aby dowiedzieć się więcej.
Jeśli preferujesz pracę offline z wykorzystaniem SublimeText, rozszerzenie glslViewer może cie zainteresować. Dowiedz się o nim więcej tutaj.
Udostępniaj: dzięki edytorowi online (editor.thebookofshaders.com/) możesz udstępniać swoje shadery! Zarówno webowa jak i stacjonarna wersja ma funkcję eksportu, generującą unikalny URL do twojego shadera. Ponadto, ma też funkcję bezpośredniego eksportu do openFrame.io.
Organizuj: udostępnianie kodu stanowi początek dzielenia się twoimi shaderowymi dziełami! Poza opcją eksportu do openFrame.io stworzyłem narzędzie do organizowania twoich shaderów w galerię, którą można wstawić na dowolną stronę internetową; nazywa się glslGallery. Kliknij tutaj, aby dowiedzieć się więcej.
Korzystanie shaderów w twoim ulubionym frameworku
Jeżeli programowanie we frameworkach jak Processing, Three.js, OpenFrameworks or SFML nie jest ci obce, to opcja wypróbowania w nich swoich shaderów zapewne cie zainteresuje. Poniżej znajdziesz przykłady, jak wykorzystać shadery w każdym z nich (z takimi samymi uniformami jak w tej książce). (W repozytorium GitHub'owym dla tego rozdziału znajdziesz pełny kod źródłowy dla tych frameworków.
W Three.js
Znakomity i bardzo skromny Ricardo Cabello (aka MrDoob ) razem z kontrybutorami tworzą prawdopodobnie najbardziej popularny framework WebGL'owy zwany Three.js. Znajdziesz wiele przykładów, tutoriali i książek, które uczą, jak wykorzstać tę JavaScriptową bibliotekę do tworzenia odjazdowej grafiki 3D.
Poniżej znajduje się przykład kodu w HTML i JS potrzebny do rozpoczęcia przygody z shaderami w three.js. Zwróć uwage na skrypt z
id="fragmentShader" - do niego możesz wstawiać kod shaderów, które znajdziesz, przykładowo, w tej książce.text/ Detailed source-code truncated for AI context efficiency. /
W Processing
Stworzony przez Ben Fry i Casey Reas w 2001, Processing jest nadzwyczaj prostym i potężnym środowiskiem, w którym możesz zacząć swoją przygodę z programowaniem (tak było w moim wypadku). Andres Colubri wniósł istotne zmiany do OpenGL i funkcji wideo w Processing, znacząco ułatwiając wykorzystanie w nim shaderów GLSL. Processing szuka pliku
shader.frag w folderze data. Zatem, jeśli chcesz spróbować uruchomić przykłady z tej książki w Processing, to pamiętaj umieścić je w tym folderze i odpowiednio nazwać. cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Aby shader działał na wersjach wcześniejszych niż 2.1, dodaj poniższą linijkę na początku shadera:
#define PROCESSING_COLOR_SHADER. Powinno to wyglądać tak: glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Jeśli chcesz wiedzieć więcej o sahderach w Processing, sprawdź ten tutorial.
W openFrameworks
Każda ma takie miejsce, gdzie czuje się najabrdziej komfortowo. W moim wypadku jest to społeczność openFrameworks. Ten C++'owy framework jest wrapperem OpenGL i innych open source'owych bibliotek C++. Pod pewnym względem jest całkiem podobny do Processing, ale z dodanym utrudnieniem radzenia sobie z kompilatorami C++.
cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Polecam addon ofxShader do openFrameworks, jeśli potrzbujesz takiego zestawu uniformów jak w GlslViewer i GlslCanvas, wsparcia dla "multiple buffering", "material shaders", hot reload'owania oraz automatycznej konwersji do OpenGL ES na Raspberry Pi. Twój kod stanie się tak prosty jak poniżej:
cpp//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
Po więcej informacji na temat shaderów w openFrameworks zajrzyj do znakomitego tutoriala autorstwa Joshua Noble.
---
04/README Pt (04/README-pt.md)
Rodando o seu shader
Como parte da construção deste livro, e minha prática de arte, eu fiz um ecossistema de ferramentas para criar, exibir e realizar curadoria de shaders. Essas ferramentas trabalham consistentemente em Linux, MacOS, Raspberry Pi e browsers sem a necessidade de mudar seu código.
Exibir: todos os exemplos ao vivo deste livro são exibidos usando o glslCanvas que faz o processo de executar um shader standalone incrivelmente fácil.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Como você pode ver, só precisa de um elemento
canvas com class="glslCanvas" e a url para seu shader no data-fragment-url. Aprenda mais sobre isso aqui.Se você é como eu, provavelmente vai querer rodar shaders diretamente da console, e nesse caso você deveria dar uma olhada no glslViewer. Esta aplicação lhe permite incorporar shaders em seus scripts
bash ou pipelines do unix e usá-los de modo similiar ao ImageMagick. Também, o glslViewer é uma boa forma de compilar shaders no seu Raspberry Pi, razão pela qual o openFrame.io o utiliza para exibir artes em shader. Aprenda mais sobre essa aplicação aqui.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Criar: de modo a trazer mais luz para a experiência de codificação de shaders, eu fiz um editor online chamado glslEditor. Este editor está embutido nos exemplos ao vivo do livro, e traz uma série de widgets bem convenientes para tornar mais tangível a experiência abstrata de trabalhar com código GLSL. Você também pode executá-lo como uma aplicação web standalone no endereço editor.thebookofshaders.com/. Saiba mais sobre isso aqui.
Se você prefere trabalhar offline usando o SublimeText você pode instalar esse pacote para glslViewer. Saiba mais sobre isso aqui.
Compartilhar: o editor online (editor.thebookofshaders.com/) pode compartilhar seus shaders! Tanto a versão embedded quanto standalone têm um botão para exportar, onde você pode obter uma URL única para o seu shader. Eles têm também a capacidade de exportar diretamente para um openFrame.io.
Curadoria: Compartilhar o seu código é o começo para você compartilhar seus shaders como artwork! Além da opção de exportar para openFrame.io eu fiz uma ferramenta para lhe permitir criar uam curadoria de seus shaders numa galeria que pode ser embutida em qualquer site, e seu nome é glslGallery. Saiba mais aqui.
Rodando seus shaders em seu framework favorito
Para o caso de você já ter experiência em programar em um framework como Processing, Three.js, OpenFrameworks ou SFML, você provavelmente está animado para tentar os shaders nessas plataformas que você se sente confortável. Os exemplos a seguir são formas de como configurar os shaders em alguns frameworks populares com os mesmos uniforms que vamos usar ao longo desse livro. (No Repositório GitHub para esse capítulo, você encontra o código completo para esses três frameworks.)
Em Three.js
O brillhante e humilde Ricardo Cabello (aka MrDoob ) tem desenvolvido com outros contribuidores o que é provavelmente um dos mais famosos frameworks para WebGL, chamado de Three.js. Você vai encontrar muitos exemplos, tutoriais e livros que ensinam a usar essa biblioteca em JavaScript para fazer gráficos 3D muito legais.
Abaixo, um exemplo do HTML e JS que você precisa para começar com shaders em three.js. Preste atenção ao script
id="fragmentShader", é aqui que você pode copiar os shaders que encontrar neste livro.text/ Detailed source-code truncated for AI context efficiency. /
Em Processing
Iniciado por Ben Fry e Casey Reas em 2001, o Processing é um ambiente extraordinariamente simples e poderoso para você dar seus primeiros passos no código (pelo menos, pra mim, foi). Andres Colubri tem feito atualizações importantes no openGL e video em Processing, facilitando ainda mais que nunca o uso e diversão com shaders GLSL nesse ambiente amigável. O Processing vai procurar pelo shader chamado
"shader.frag" na pasta data. Tenha certeza de copiar os exemplos que encontrar aqui para essa pasta, e renomear o arquivo.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
De modo a fazer o shader funcionar em versões anteriores à 2.1, você precisa de adicionar a seguinte linha no começo de seu shader:
#define PROCESSING_COLOR_SHADER. De modo que se pareça com isso:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Para mais informações sobre os shaders no Processing veja esse tutorial.
No openFrameworks
Todo mundo tem um lugar onde se sente confortável, e no meu caso, ainda é a comunidade openFrameworks. Esse framework C++ envolve o OpenGL e outras bibliotecas opensource C++. De muitas formas, é bem parecida com o Processing, mas com as complicações óbvias de se lidar com compiladores C++. Da mesma forma que o Processing, openFrameworks vai procurar o seu shader no diretório data, então não se esqueça de copiar os arquivos
.frag que quiser usar, e mudar o nome quando for carregar.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Para mais informações sobre shaders em openFrameworks, vá nesse excelente tutorial feito por Joshua Noble.
---
04/README Ru (04/README-ru.md)
Запуск шейдера
В процессе создания этой книги и просто из любви к искусству я создал набор инструментов для создания, отображения, опубликования и сопровождения шейдеров. Эти инструменты работают одинаково на Linux, MacOS, Raspberry Pi и в браузерах без переписывания кода.
Отображение: все интерактивные примеры в книге показываются с помощью glslCanvas, который делает процесс запуска отдельных шейдеров невероятно простым.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Как видите, для этого нужен всего лишь элемент
canvas с классом class="glslCanvas" и URL шейдера в свойстве data-fragment-url. Подробнее о нём можно узнать здесь.Если вы разделяете мой подход к разработке, вам возможно захочется запускать шейдеры напрямую из консоли, в чём вам поможет glslViewer. Это приложение позволяет встраивать шейдеры в
bash-скрипты или конвейер unix, и может использоваться по аналогии с ImageMagick. Так же, glslViewer - отличный способ скомпилировать шейдеры на Raspberry Pi, и поэтому он используется на openFrame.io для демонстрации шейдеров. Подробнее с этим приложением можно познакомиться здесь.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Создание: чтобы сделать опыт создания шейдеров более ярким, я сделал онлайн-редактор glslEditor. Этот редактор используется для демонстрации интерактивных примеров в книге и предоставляет набор виджетов, делающих абстрактный код на GLSL более осязаемым. Его так же можно запустить в отдельном веб-приложении по адресу editor.thebookofshaders.com. Подробнее о редакторе здесь.
Если вы предпочитаете работать оффлайн в редакторе SublimeText, вы можете установить пакет для glslViewer. Подробнее здесь.
Публикация: онлайн-редактор (editor.thebookofshaders.com/) может опубликовать ваш шейдер! Как встраиваемая, так и отдельная версия содержат кнопку «экспорт», которая выдаёт уникальные URL каждому шейдеру. Так же есть возможность выгружать шейдеры сразу на openFrame.io.
Сопровождение: Публикация кода - это только первый шаг на пути вашего шейдера в качестве художественного произведения. Помимо возможности экспорта на openFrame.io, я создал инструмент glslGallery для размещения шейдеров в галерее, которую можно встроить на любой сайт. Подробнее здесь.
Запуск шейдера в вашем любимом фреймворке
Если у вас уже есть опыт программирования на таких фреймворках, как Processing, Three.js, OpenFrameworks или SFML, вы можете попробовать шейдеры прямо в них. Ниже показаны способы установки используемых в книге uniform-переменных на некоторых популярных фреймворках. В репозитории этой главы на Гитхабе вы найдёте полный исходный код для этих трёх фреймворков.
Three.js
Рикардо Кабелло (aka MrDoob ) и группа единомышленников разработали один из лучших WebGL-фреймворков под названием Three.js. Там вы найдёте множество примеров, учебных курсов и книг с помощью которых можно создавать крутую 3D-графику на JavaScript.
Ниже вы видите минимальный пример HTML и JS-кода, позволяющего начать использовать шейдеры на three.js. Обратите внимание на скрипт с
id="fragmentShader". Именно благодаря ему вы можете копировать шейдеры из книги.text/ Detailed source-code truncated for AI context efficiency. /
Processing
Processing - необычайно простая и мощная среда, созданная Беном Фраем и Кэси Рис в 2001 году. Она хорошо подходит для того, чтобы сделать ваши первые шаги в программировании (по крайней мере, так было у меня). Андре Колубри добавил в Processing поддержку OpenGL и видео, из-за чего играть с шейдерами в ней стало проще простого. Processing ищет файл с именем
shader.frag в папке data вашего скетча. Таким образом, вы можете просто скопировать пример отсюда и переименовать файл.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
shader.set("u_resolution", float(width), float(height));
}
void draw() {
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Чтобы шейдеры запустились на версиях ниже 2.1, добавьте строку
#define PROCESSING_COLOR_SHADER в начало шейдера. В итоге, код шейдера будет выглядеть примерно так:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Более подробную информацию о шейдерах в Processing ищите в этом руководстве.
openFrameworks
У каждого человека есть место, где ему комфортно, и для меня таковым остаётся сообщество openFrameworks. Этот C++-фреймворк является обёрткой для OpenGL и других библиотек на C++. Он во многом похож на Processing с поправкой на особенности использования компиляторов языка C++. Как и Processing, openFramework ищет шейдеры в папке
data, поэтому не забудьте скопировать в него и файлы с расширением .frag и переименовывать их перед использованием.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
За более подробной информацией о шейдерах в openFrameworks обратитесь к этому руководству от Joshua Noble.
---
04/README Tr (04/README-tr.md)
Shader'ınızı çalıştırma
Bu kitabın yapımı ve sanat pratiğimin bir parçası olarak, shader'ları oluşturmak, görüntülemek, paylaşmak ve küratörlüğünü yapmak için bir araç ekosistemi oluşturdum. Bu araçlar Linux, MacOS, Windows ve Raspberry Pi ile tarayıcılarda, kodunuzu değiştirmenize gerek kalmadan tutarlı bir şekilde çalışır.
Shader'larınızı tarayıcıda çalıştırma
Görüntüleme: Bu kitaptaki tüm canlı örnekler, bağımsız shader çalıştırma sürecini inanılmaz derecede kolaylaştıran glslCanvas kullanılarak görüntülenir.
html<canvas class="glslCanvas" data-fragment-url="yourShader.frag" data-textures="yourInputImage.png" width="500" height="500"></canvas>
Gördüğünüz gibi, sadece
class="glslCanvas" özelliğine ve data-fragment-url'de shader'ınızın URL'sine sahip bir canvas öğesine ihtiyaç duyar. Daha fazla bilgi için buraya bakabilirsiniz.Eğer benim gibi biriyseniz, muhtemelen shader'ları doğrudan konsoldan çalıştırmak isteyeceksiniz, bu durumda glslViewer'a göz atmalısınız. Bu uygulama, shader'ları
bash scriptlerinize veya unix aktarım hatlarına dahil etmenizi ve ImageMagick ile benzer şekilde kullanmanızı sağlar. Ayrıca glslViewer, shader'ları Raspberry Pi üzerinde derlemek için harika bir yoldur; bu nedenle openFrame.io shader sanat eserlerini görüntülemek için onu kullanır. Bu uygulama hakkında daha fazla bilgi için buraya bakabilirsiniz.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Oluşturma: Shader kodlama deneyimini aydınlatmak için glslEditor adında çevrimiçi bir düzenleyici yaptım. Bu düzenleyici kitabın canlı örneklerine gömülüdür ve glsl koduyla çalışmanın soyut deneyimini daha somut hale getirmek için bir dizi kullanışlı widget sunar. Ayrıca bunu editor.thebookofshaders.com/ adresinden bağımsız bir web uygulaması olarak da çalıştırabilirsiniz. Daha fazla bilgi için buraya bakabilirsiniz.
Çevrimdışı çalışmayı tercih ediyorsanız ve SublimeText kullanıyorsanız, bu glslViewer paketini kurabilirsiniz. Daha fazla bilgi için buraya bakabilirsiniz.
Paylaşma: Çevrimiçi düzenleyici (editor.thebookofshaders.com/) shader'larınızı paylaşabilir! Hem gömülü hem de bağımsız sürümde, shader'ınıza özgü bir URL almanızı sağlayan bir dışa aktarma düğmesi bulunur. Ayrıca doğrudan bir openFrame.io'ya dışa aktarma yeteneğine de sahiptir.
Küratörlük: Kodunuzu paylaşmak, shader'ınızı bir sanat eseri olarak paylaşmanın başlangıcıdır! openFrame.io'ya dışa aktarma seçeneğinin yanı sıra, shader'larınızı herhangi bir siteye gömülebilecek bir galeriye dönüştürmek için glslGallery adlı bir araç yaptım. Daha fazla bilgi için buraya bakabilirsiniz.
Shader'larınızı favori framework'ünüzde çalıştırma
Processing, Three.js, OpenFrameworks veya SFML gibi bir framework'te programlama deneyiminiz varsa, muhtemelen rahat hissettiğiniz bu platformlarda shader'ları denemekten heyecan duyarsınız. Aşağıda, bu kitap boyunca kullanacağımız aynı uniform'larla bazı popüler framework'lerde shader'ların nasıl kurulacağına dair örnekler bulunmaktadır. (Bu bölümün GitHub deposunda, bu üç framework için tam kaynak kodunu bulacaksınız.)
Three.js'de
Parlak ve çok alçakgönüllü Ricardo Cabello (namıdiğer MrDoob), diğer katkıda bulunanlarla birlikte muhtemelen WebGL için en ünlü framework'lerden birini geliştiriyor: Three.js. Harika 3D grafikler yapmak için bu JavaScript kütüphanesini nasıl kullanacağınızı öğreten birçok örnek, öğretici ve kitap bulacaksınız.
Aşağıda, three.js'de shader'larla başlamak için ihtiyacınız olan HTML ve JS örneği yer almaktadır.
id="fragmentShader" script'ine dikkat edin, bu kitapta bulduğunuz shader'ları buraya kopyalayabilirsiniz.text/ Detailed source-code truncated for AI context efficiency. /
Processing'de
2001 yılında Ben Fry ve Casey Reas tarafından başlatılan Processing, kodlamada ilk adımlarınızı atmak için son derece basit ve güçlü bir ortamdır (en azından benim için öyleydi). Andres Colubri, Processing'de OpenGL ve video konusunda önemli güncellemeler yaparak, bu dostane ortamda GLSL shader'larını kullanmayı ve denemeyi her zamankinden daha kolay hale getirdi. Processing, sketch'in
data klasöründe "shader.frag" adlı shader'ı arayacaktır. Burada bulduğunuz örnekleri o klasöre kopyaladığınızdan ve dosya adını değiştirdiğinizden emin olun.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Shader'ın 2.1'den önceki sürümlerde çalışması için, shader'ınızın başına şu satırı eklemeniz gerekir:
#define PROCESSING_COLOR_SHADER. Böylece şöyle görünmelidir:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Processing'de shader'lar hakkında daha fazla bilgi için bu öğreticiye göz atın.
openFrameworks'de
Herkesin rahat hissettiği bir yer vardır, benim için bu hâlâ openFrameworks topluluğudur. Bu C++ framework'ü OpenGL ve diğer açık kaynak C++ kütüphanelerinin etrafını sarar. Birçok yönden Processing'e çok benzer, ancak C++ derleyicileriyle uğraşmanın bariz karmaşıklıkları vardır. Processing ile aynı şekilde, openFrameworks shader dosyalarınızı data klasöründe arayacaktır, bu yüzden kullanmak istediğiniz
.frag dosyalarını kopyalayıp yüklerken adını değiştirmeyi unutmayın.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
GlslViewer ve GlslCanvas spesifikasyonlarında bulunan tüm uniform'ları OpenFrameworks'de daha basit bir şekilde kullanmak istiyorsanız, ofxShader eklentisini kullanmanızı öneririm; bu eklenti aynı zamanda birden fazla tampon, malzeme shader'ları, anında yeniden yükleme ve Raspberry Pi'de OpenGL ES için otomatik dönüştürme desteğine de sahip olacaktır. Ve kodunuz şu kadar basit olacaktır:
cpp//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
openFrameworks'te shader'lar hakkında daha fazla bilgi için Joshua Noble tarafından hazırlanan bu mükemmel öğreticiye gidin.
---
04/README Ua (04/README-ua.md)
Запуск шейдера
У процесі створення цієї книги та моєї художньої практики, я сформував екосистему інструментів для створення шейдерів, їх відображення, спільного використання та керування. Ці інструменти працюють однаково в Linux, MacOS, Windows, Raspberry Pi та браузерах без необхідності змінювати код.
Запуск шейдерів у браузері
Відображення: усі інтерактивні приклади в цій книзі відображаються за допомогою glslCanvas, що робить процес запуску автономного шейдера неймовірно простим.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Як бачите, для цього потрібен лише елемент
canvas із відповідним класом class="glslCanvas" та URL-адресою вашого шейдера, заданої в атрибуті data-fragment-url. Дізнатися більше про це можна тут.Якщо ви схожі на мене, ви, ймовірно, захочете запускати шейдери безпосередньо з консолі. У такому випадку вам допоможе glslViewer. Ця програма дозволяє вам включати шейдери у ваші
bash-скрипти або unix-конвеєри та використовувати їх подібно до ImageMagick. Також glslViewer є чудовим способом компіляції шейдерів на Raspberry Pi, через що openFrame.io використовує його для демонстрації шейдерних робіт. Дізнайтеся більше про цю програму за цим посиланням.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Створення: щоб покращити досвід кодування шейдерів, я створив онлайн-редактор під назвою glslEditor. Цей редактор використовується в інтерактивних прикладах книги та пропонує низку зручних віджетів, що робить взаємодію з абстрактним кодом glsl більш відчутним. Ви також можете запустити його як окрему вебпрограму: editor.thebookofshaders.com. Дізнатися більше про редактор можна тут.
Якщо ви бажаєте працювати з редактором офлайн за допомогою SublimeText, то можете встановити цей пакет для glslViewer. Дізнайтеся більше тут.
Експортування: онлайн-редактор (editor.thebookofshaders.com) може поділитися вашими шейдерами! Як вбудована, так і окрема версія мають кнопку для експорту, за допомогою якої ви можете отримати унікальну URL-адресу свого шейдера. Також є можливість експортувати шейдер безпосередньо в openFrame.io.
Керування: експорт коду — початок для того, щоб ви ділилися своїм шейдером у якості художнього витвору! Окрім опції експорту в openFrame.io, я створив інструмент для керування вашими шейдерами у галереї, яку можна вбудувати у будь-який сайт — glslGallery. Детальніше тут.
Запуск ваших шейдерів у вашому улюбленому фреймворку
Якщо у вас уже є досвід програмування в таких фреймворках, як Processing, Three.js, OpenFrameworks або SFML, ви, мабуть, із задоволенням спробуєте шейдери у них. Нижче наведено приклади того, як налаштувати шейдери в деяких популярних фреймворках з вказаними у книзі uniform-змінними. У GitHub-репозиторії для цього розділу ви знайдете повний вихідний код для цих трьох фреймворків.
Three.js
Блискучий і дуже скромний Ricardo Cabello (він же MrDoob) з групою інших однодумців розробили один із найвідоміших фреймворків для WebGL під назвою Three.js. Там ви знайдете безліч прикладів, посібників і книг, які навчать вас, як використовувати цю JS-бібліотеку для створення класної 3D-графіки.
Нижче наведено приклад із HTML та JS, які необхідні для початку роботи з шейдерами у three.js. Зверніть увагу на скрипт з
id="fragmentShader" куди ви можете скопіювати шейдери, знайдені в цій книзі.text/ Detailed source-code truncated for AI context efficiency. /
Processing
Processing започатковано у 2001 році у співпраці між Ben Fry та Casey Reas. Фреймворк є надзвичайно простим та потужним середовищем, у якому можна робити свої перші кроки в програмуванні (принаймні так було у мене). Andres Colubri вніс важливі оновлення в Processing для підтримки openGL і відео, що полегшило використання шейдерів GLSL у цьому дружньому середовищі, ніж будь-коли. Processing шукатиме шейдер із назвою
"shader.frag" у теці data вашого скетчу. Тож вам достатньо скопіювати приклади цієї книги у файл із відповідною назвою.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Щоб шейдер працював у попередніх версіях, нижчих за 2.1, вам потрібно додати рядок
#define PROCESSING_COLOR_SHADER на початку вашого шейдера. Це виглядатиме так:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st / u_resolution;
gl_FragColor = vec4(st.x, st.y, 0.0, 1.0);
}
Щоб дізнатися більше про шейдери у Processing, перегляньте цей посібник.
openFrameworks
У кожного є місце, де йому комфортно, у моєму випадку таким місцем залишається спільнота openFrameworks. Цей C++ фреймворк є обгорткою навколо OpenGL та інших бібліотек на C++ з відкритим кодом. Багато в чому він дуже схожий на Processing, але зі своїми особливостями роботи з використанням компіляторів C++. Так само як і Processing, openFrameworks шукатиме ваші файли шейдерів у теці
data. Тому не забудьте скопіювати код прикладів, які хочете використати, у файли з розширенням .frag і покласти їх у згадану теку:cppvoid ofApp::draw() {
ofShader shader;
shader.load("", "shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0, 0, ofGetWidth(), ofGetHeight());
shader.end();
}
Якщо ви хочете використовувати в OpenFrameworks повний набір уніформ, що містяться в специфікаціях GlslViewer і GlslCanvas, простішим способом, я рекомендую використовувати доповнення ofxShader. Воно також має підтримку кількох буферів, шейдерних матеріалів, гаряче перезавантаження та автоматизацію для OpenGL ES у Raspberry Pi. Ваш код буде таким же простим, як нижче:
cpp//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
Щоб дізнатися більше про шейдери в openFrameworks, перегляньте цей чудовий посібник, створений Joshua Noble.
---
04/README Vi (04/README-vi.md)
Chạy thử shader
Trong quá trình hình thành nên quyển sách này tôi đã tạo ra hàng loạt các công cụ có thể viết, hiển thị, chia sẻ và tìm duyệt các shader. Các công cụ này hoạt động giống nhau trên cả Linux, MacOS, Windows, Raspberry Pi và cả trình duyệt nữa, mà không cần phải chỉnh sửa gì thêm.
Chạy thử shader trên trình duyệt
Hiển thị: Toàn bộ ví dụ tương tác được trong quyển sách này đều được hiển thị bằng glslCanvas để preview shader theo cách vô cùng đơn giản.
html<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
Như bạn thấy, chỉ cần một thẻ
canvas với class="glslCanvas" và URL tới shader ở thuộc tính data-fragment-url là đủ. Tìm hiểu thêm tại đây.Nếu bạn giống tôi, bạn có thể sẽ muốn preview shader ngay trong console, và trong trường hợp thì bạn nên ngó qua glslViewer. Ứng dụng này cho phép bạn kết hợp shader với script
bash hay bất kỳ công cụ dòng lệnh nào của Unix giống như cách mà ImageMagick hoạt động. Ngoài ra glslViewer cũng là một cách hay để biên dịch shader trên Raspberry Pi, cũng chính là lí do mà openFrame.io dùng nó để preview shader. Tìm hiểu thêm về công cụ này tại đây.bashglslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
Viết shader: để chia sẻ kinh nghiệm code shader, tôi đã tạo một editor online gọi là glslEditor. Editor này được nhúng vào các ví dụ trong quyển sách này, nó bổ sung các widget rất tiện cho việc viết code GLSL. Bạn cũng có thể chạy riêng nó như một ứng dụng web ở editor.thebookofshaders.com/. Tìm hiểu thêm tại đây.
Nếu bạn thích làm việc offline bằng SublimeText bạn có thể cài đặt package glslViewer. Tìm hiểu thêm tại đây.
Chia sẻ: Bạn có thể chia sẻ shader bằng editor online (editor.thebookofshaders.com/)! Cả phiên bản nhúng và chạy độc lập đều có nút bấm export cho bạn một URL tới shader của mình. Nó còn có khả năng export trực tiếp tới openFrame.io.
Thư viện: Bên cạnh việc export sang openFrame.io Tôi còn tạo ra công cụ để bạn có thể khoe shader của mình trên bất kỳ site nào, tên nó là glslGallery. Tìm hiểu thêm tại đây.
Chạy shader trên framework yêu thích
Trong trường hợp bạn đã có kinh nghiệm lập trình với các framework như: Processing, Three.js, OpenFrameworks hay SFML, you có thể sẽ muốn thử shader trên các nền tảng đó. Dưới đây là hướng dẫn sử dụng shader trên các framework phổ biến với các uniform giống như shader được viết trong quyển sách này. (Trong repository GitHub cho chương này, bạn có thể tìm thấy mã nguồn hoàn chỉnh của cả 3 framework này).
Với Three.js
Ricardo Cabello (hay còn gọi là MrDoob ), một anh chàng thông minh và khiêm tốn đã phát triển một trong những framework nổi tiếng nhất cho WebGL cùng với các cộng sự, đó là Three.js. Bạn sẽ tìm thấy rất nhiều ví dụ, hướng dẫn và sách dạy bạn cách dùng thư viện JavaScript này để tạo nên các tác phẩm đồ hoạ 3D rất ngầu.
Dưới đây là một ví dụ về code HTML và JS mà bạn cần để bắt đầu viết shader với three.js. Hãy chú ý tới đoạn
id="fragmentShader", đây là nơi mà bạn có thể copy-paste shader từ quyển sách này thay vào đó để thử.text/ Detailed source-code truncated for AI context efficiency. /
Với Processing
Bắt đầu bởi Ben Fry và Casey Reas năm 2001, Processing là môi trường siêu đơn giản mà mạnh mẽ để bắt đầu code shader (ít nhất là với tôi). Andres Colubri đã cải tiến OpenGL và Video trong Processing để khiến cho việc sử dụng GLSL trở nên dễ hơn bao giờ hết. Processing sẽ thực thi file shader có tên
"shader.frag" trong thư mục data. Nếu bạn copy ví dụ ở đây, hãy nhớ đổi tên file cho khớp.cppPShader shader;void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
Để shader hoạt động với các phiên bản trước 2.1, bạn cần thêm dòng sau vào phần đầu của shader:
#define PROCESSING_COLOR_SHADER. Nó sẽ trông như thế này:glsl#ifdef GL_ES
precision mediump float;
#endif#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st/u_resolution;
gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}
Để biết thêm về shader trong Processing, hãy xem hướng dẫn.
Với openFrameworks
Ai cũng có công cụ mà họ cảm thấy thoải mái nhất, trong trường hợp của tôi thì đó là openFrameworks. Framework viết bằng C++ này dựa trên OpenGL và một vài thư viện mã nguồn mở khác. Nó rất giống Processing ở nhiều điểm, chỉ khác ở phần biên dịch C++ phức tạp. Giống như Processing, openFrameworks sẽ tìm file shader của bạn để thực thi, nên nếu copy file
.frag nào ở đây thì đừng quên đổi tên file trong code cho khớp.cppvoid ofApp::draw(){
ofShader shader;
shader.load("","shader.frag"); shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0,0,ofGetWidth(), ofGetHeight());
shader.end();
}
Nếu bạn muốn sử dụng bộ đầy đủ các uniform trong GlslViewer và GlslCanvas trong openFrameworks một cách đơn giản, tôi xin giới thiệu addon ofxShader để hỗ trợ nhiều buffer, chất liệu, hot-reload và tự động chuyển đổi sang OpenGL ES trên Raspberry Pi. Code đơn giản như sau:
cpp//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
Để tìm hiểu thêm về shader trong openFrameworks hãy đọc bài hướng dẫn tuyệt vời này của Joshua Noble.
---
04/Sfml/CMakeLists (04/sfml/CMakeLists.txt)
cmake_minimum_required(VERSION 3.15)
project(TheBookOfShaders LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(SFML 2.5 COMPONENTS graphics REQUIRED)
add_executable(TheBookOfShaders
src/main.cpp
)
target_link_libraries(TheBookOfShaders sfml-graphics)
install(TARGETS TheBookOfShaders
RUNTIME DESTINATION .)
install(DIRECTORY data
DESTINATION .)
---
05/README (05/README.md)
Algorithmic drawing
Shaping functions
This chapter could be named "Mr. Miyagi's fence lesson." Previously, we mapped the normalized position of x and y to the red and green channels. Essentially we made a function that takes a two dimensional vector (x and y) and returns a four dimensional vector (r, g, b and a). But before we go further transforming data between dimensions we need to start simpler... much simpler. That means understanding how to make one dimensional functions. The more energy and time you spend learning and mastering this, the stronger your shader karate will be.
The following code structure is going to be our fence. In it, we visualize the normalized value of the x coordinate (
st.x) in two ways: one with brightness (observe the nice gradient from black to white) and the other by plotting a green line on top (in that case the x value is assigned directly to y). Don't focus too much on the plot function, we will go through it in more detail in a moment.<div class="codeAndCanvas" data="linear.frag"></div>
Quick Note: The
vec3 type constructor "understands" that you want to assign the three color channels with the same value, while vec4 understands that you want to construct a four dimensional vector with a three dimensional one plus a fourth value (in this case the value that controls the alpha or opacity). See for example lines 19 and 25 above.This code is your fence; it's important to observe and understand it. You will come back over and over to this space between 0.0 and 1.0. You will master the art of blending and shaping this line.
This one-to-one relationship between x and y (or the brightness) is known as linear interpolation. From here we can use some mathematical functions to shape the line. For example we can raise x to the power of 5 to make a curved line.
<div class="codeAndCanvas" data="expo.frag"></div>
Interesting, right? On line 22 try different exponents: 20.0, 2.0, 1.0, 0.0, 0.2 and 0.02 for example. Understanding this relationship between the value and the exponent will be very helpful. Using these types of mathematical functions here and there will give you expressive control over your code, a sort of data acupuncture that let you control the flow of values.
pow() is a native function in GLSL and there are many others. Most of them are accelerated at the level of the hardware, which means if they are used in the right way and with discretion they will make your code faster.exp(), log() and sqrt(). Some of these functions are more interesting when you play with them using PI. You can see on line 8 that I have defined a macro that will replace any use of PI with the value 3.14159265359.Step and Smoothstep
GLSL also has some unique native interpolation functions that are hardware accelerated.
step() interpolation receives two parameters. The first one is the limit or threshold, while the second one is the value we want to check or pass. Any value under the limit will return 0.0 while everything above the limit will return 1.0.Try changing this threshold value on line 20 of the following code.
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep(). Given a range of two numbers and a value, this function will interpolate the value between the defined range. The two first parameters are for the beginning and end of the transition, while the third is for the value to interpolate.<div class="codeAndCanvas" data="smoothstep.frag"></div>
In the previous example, on line 12, notice that we’ve been using smoothstep to draw the green line on the
plot() function. For each position along the x axis this function makes a bump at a particular value of y. How? By connecting two smoothstep() together. Take a look at the following function, replace it for line 20 above and think of it as a vertical cut. The background does look like a line, right?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sine and Cosine
When you want to use some math to animate, shape or blend values, there is nothing better than being friends with sine and cosine.
These two basic trigonometric functions work together to construct circles that are as handy as MacGyver’s Swiss army knife. It’s important to know how they behave and in what ways they can be combined. In a nutshell, given an angle (in radians) they will return the correct position of x (cosine) and y (sine) of a point on the edge of a circle with a radius equal to 1. But, the fact that they return normalized values (values between -1 and 1) in such a smooth way makes them an incredible tool.
While it's difficult to describe all the relationships between trigonometric functions and circles, the above animation does a beautiful job of visually summarizing these relationships.
<div class="simpleFunction" data="y = sin(x);"></div>
Take a careful look at this sine wave. Note how the y values flow smoothly between +1 and -1. As we saw in the time example in the previous chapter, you can use this rhythmic behavior of
sin() to animate properties. If you are reading this example in a browser you will see that you can change the code in the formula above to watch how the wave changes. (Note: don't forget the semicolon at the end of the lines.)Try the following exercises and notice what happens:
Add time (
u_time) to x before computing the sin. Internalize that motion along x*. Multiply x* by
PI before computing the sin. Note how the two phases shrink so each cycle repeats every 2 integers. Multiply time (
u_time) by x* before computing the sin. See how the frequency between phases becomes more and more compressed. Note that u_time may have already become very large, making the graph hard to read.sin(x). See how all the wave is displaced up and now all values are between 0.0 and 2.0.sin(x) by 2.0. See how the amplitude doubles in size.abs()) of sin(x). It looks like the trace of a bouncing ball.fract()) of the resultant of sin(x).ceil()) and the smaller integer (floor()) of the resultant of sin(x) to get a digital wave of 1 and -1 values.Some extra useful functions
At the end of the last exercise we introduced some new functions. Now it’s time to experiment with each one by uncommenting the lines below one at a time. Get to know these functions and study how they behave. I know, you are wondering... why? A quick google search on "generative art" will tell you. Keep in mind that these functions are our fence. We are mastering the movement in one dimension, up and down. Soon, it will be time for two, three and four dimensions!
<div class="simpleFunction" data="y = mod(x,0.5); // return x modulo of 0.5
//y = fract(x); // return only the fraction part of a number
//y = ceil(x); // nearest integer that is greater than or equal to x
//y = floor(x); // nearest integer less than or equal to x
//y = sign(x); // extract the sign of x
//y = abs(x); // return the absolute value of x
//y = clamp(x,0.0,1.0); // constrain x to lie between 0.0 and 1.0
//y = min(0.0,x); // return the lesser of x and 0.0
//y = max(0.0,x); // return the greater of x and 0.0 "></div>
Advance shaping functions
Golan Levin has great documentation of more complex shaping functions that are extraordinarily helpful. Porting them to GLSL is a really smart move, to start building your own resource of snippets of code.
* Polynomial Shaping Functions: www.flong.com/archive/texts/code/shapers_poly
* Exponential Shaping Functions: www.flong.com/archive/texts/code/shapers_exp
* Circular & Elliptical Shaping Functions: www.flong.com/archive/texts/code/shapers_circ
* Bezier and Other Parametric Shaping Functions: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Like chefs that collect spices and exotic ingredients, digital artists and creative coders have a particular love of working on their own shaping functions.
Iñigo Quiles has a great collection of useful functions. After reading this article take a look at the following translation of these functions to GLSL. Pay attention to the small changes required, like putting the "." (dot) on floating point numbers and using the GLSL name for C functions; for example instead of
powf() use pow():<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
To keep your motivation up, here is an elegant example (made by Danguafer) of mastering the shaping-functions karate.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
In the Next >> chapter we will start using our new moves. First with mixing colors and then drawing shapes.
#### Exercise
Take a look at the following table of equations made by Kynd. See how he is combining functions and their properties to control the values between 0.0 and 1.0. Now it's time for you to practice by replicating these functions. Remember the more you practice the better your karate will be.
#### For your toolbox
* LYGIA is a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks. It's divided in different sections and it have an entire one for math operations
* GraphToy: once again Iñigo Quilez made a tool to visualize GLSL functions in WebGL.
---
05/Notes (05/notes.md)
Scott
* _"Essentially we made a function that takes a two dimensional vector (x and y) and returns a four dimensional vector (r, g, b and a)."_ Really? Are you sure that’s what we did?
* What is smoothstep()? This example starts to lose me. There are many new elements here. Could you include a step by step walkthrough of this whole example before proceeding to the next part?
* Actually, this is the point where you lose me. I want it to be more gradual. It feels like there are 100 new concepts on this one page.
Nicolas
* took the freedom to explain more indepth what interpolation is and why it rocks :)---
05/README Ch (05/README-ch.md)
算法绘画
造型函数
这一章应该叫做宫城先生的粉刷课(来自电影龙威小子的经典桥段)。之前我们把规范化后的 x,y 坐标映射(map)到了红色和绿色通道。本质上说我们是建造了这样一个函数:输入一个二维向量(x,y),然后返回一个四维向量(r,g,b,a)。但在我们跨维度转换数据之前,我们先从更加…更加简单的开始。我们来建一个只有一维变量的函数。你花越多的时间和精力在这上面,你的 shader 功夫就越厉害。
接下来的代码结构就是我们的基本功。在它之中我们对规范化的 x 坐标(
`st.x`)进行可视化。有两种途径:一种是用亮度(度量从黑色到白色的渐变过程),另一种是在顶层绘制一条绿色的线(在这种情况下 x 被直接赋值给 y)。不用过分在意绘制函数,我们马上会更加详细地解释它。<div class="codeAndCanvas" data="linear.frag"></div>
简注 :
`vec3` 类型构造器“明白”你想要把一个值赋值到颜色的三个通道里,就像 `vec4` 明白你想要构建一个四维向量,三维向量加上第四个值(比如颜色的三个值加上透明度)。请参照上面示例的第 19 到 25 行。这些代码就是你的基本功;遵守和理解它非常重要。你将会一遍又一遍地回到 0.0 到 1.0 这个区间。你将会掌握融合与构建这些代码的艺术。
这些 x 与 y(或亮度)之间一对一的关系称作线性插值(linear interpolation)。(译者注:插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值。因为对计算机来说,屏幕像素是离散的而不是连续的,计算机图形学常用插值来填充图像像素之间的空隙。)现在起我们可以用一些数学函数来改造这些代码行。比如说我们可以做一个求 x 的 5 次幂的曲线。
<div class="codeAndCanvas" data="expo.frag"></div>
很有趣,对吧?试试看把第 22 行的指数改为不同的值,比如:20.0,2.0,1.0,0.0,0.2 或 0.02。理解值和指数之间的关系非常重要。这些数学函数可以让你灵动地控制你的代码,就像是给数据做针灸一样。
`pow()` (求x的y次幂)是 GLSL 的一个原生函数,GLSL 有很多原生函数。大多数原生函数都是硬件加速的,也就是说如果你正确使用这些函数,你的代码就会跑得更快。`exp()`(以自然常数e为底的指数函数),`log()`(对数函数) 和 `sqrt()`(平方根函数)。当你用 Pi 来玩的时候有些方程会变得更有趣。在第 5 行我定义了一个宏,使得每当程序调用 `PI` 的时候就用 `3.14159265359` 来替换它。Step 和 Smoothstep
GLSL 还有一些独特的原生插值函数可以被硬件加速。
`step()` 插值函数需要输入两个参数。第一个是极限或阈值,第二个是我们想要检测或通过的值。对任何小于阈值的值,返回 `0.0`,大于阈值,则返回 `1.0`。试试看改变下述代码中第 20 行的值。
<div class="codeAndCanvas" data="step.frag"></div>
`smoothstep()`。当给定一个范围的上下限和一个数值,这个函数会在已有的范围内给出插值。前两个参数规定转换的开始和结束点,第三个是给出一个值用来插值。<div class="codeAndCanvas" data="smoothstep.frag"></div>
在之前的例子中,注意第 12 行,我们用到 smoothstep 在
`plot()` 函数中画了一条绿色的线。这个函数会对给出的 x 轴上的每个值,在特定的 y 值处制造一个凹凸形变。如何做到呢?通过把两个 `smoothstep()` 连接到一起。来看看下面这个函数,用它替换上面的第 20 行,把它想成是一个垂直切割。背景看起来很像一条线,不是吗?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
正弦和余弦函数
当你想用数学来制造动效,形态或去混合数值,sin 和 cos 就是你的最佳伙伴。
这两个基础的三角函数是构造圆的极佳工具,就像张小泉的剪刀一样称手。很重要的一点是你需要知道它们是如何运转的,还有如何把它们结合起来。简单来说,当我们给出一个角度(这里采用弧度制),它就会返回半径为一的圆上一个点的 x 坐标(cos)和 y 坐标(sin)。正因为 sin 和 cos 返回的是规范化的值(即值域在 -1 和 1 之间),且如此流畅,这就使得它成为一个极其强大的工具。
尽管描述三角函数和圆的关系是一件蛮困难的事情,上图动画很棒地做到了这一点,视觉化展现了它们之间的关系。
<div class="simpleFunction" data="y = sin(x);"></div>
仔细看 sin 曲线。观察 y 值是如何平滑地在 +1 和 -1 之间变化。就像之前章节关于的 time 的例子中,你可以用
`sin()` 的有节奏的变动给其他东西加动效。如果你是在用浏览器阅读的话你可以改动上述公式,看看曲线会如何变动。(注:不要忘记每行最后要加分号!)试试下面的小练习,看看会发生什么:
* 在
`sin` 里让 x 加上时间(`u_time`)。让sin 曲线随 x 轴动起来。* 在
`sin` 里用 `PI` 乘以 x。注意 sin 曲线上下波动的两部分如何收缩了,现在 sin 曲线每两个整数循环一次。* 在
`sin` 里用时间( `u_time`)乘以 x。观察各阶段的循环如何变得越来越频繁。注意 u_time 可能已经变得非常大,使得图像难以辨认。`sin(x)`(注意不是 sin 里的 x)加 1.0。观察曲线是如何向上移动的,现在值域变成了 0.0 到 2.0。`sin(x)` 乘以 2.0。观察曲线大小如何增大两倍。* 计算
`sin(x)` 的绝对值(`abs()`)。现在它看起来就像一个弹力球的轨迹。* 只选取
`sin(x)` 的小数部分(`fract()`)。`ceil()`)和向负无穷取整(`floor()`),使得 sin 曲线变成只有 1 和 -1 的电子波。其他有用的函数
最后一个练习中我们介绍了一些新函数。现在我们来一个一个试一遍。依次取消注释下列各行,理解这些函数,观察它们是如何运作的。你一定在奇怪……为什么要这么做呢?Google 一下“generative art”(生成艺术)你就知道了。要知道这些函数就是我们的栅栏。我们现在控制的是它在一维中的移动,上上下下。很快,我们就可以尝试二维、三维甚至四维了!
<div class="simpleFunction" data="y = mod(x,0.5); // 返回 x 对 0.5 取模的值
//y = fract(x); // 仅仅返回数的小数部分
//y = ceil(x); // 向正无穷取整
//y = floor(x); // 向负无穷取整
//y = sign(x); // 提取 x 的正负号
//y = abs(x); // 返回 x 的绝对值
//y = clamp(x,0.0,1.0); // 把 x 的值限制在 0.0 到 1.0
//y = min(0.0,x); // 返回 x 和 0.0 中的较小值
//y = max(0.0,x); // 返回 x 和 0.0 中的较大值 "></div>
造型函数进阶
Golan Levin 写过关于更加复杂的造型函数的文档,非常有帮助。把它们引入 GLSL 是非常明智的选择,这将是你的代码的广阔的素材库
* 多项式造型函数(Polynomial Shaping Functions): www.flong.com/archive/texts/code/shapers_poly
* 指数造型函数(Exponential Shaping Functions): www.flong.com/archive/texts/code/shapers_exp
* 圆与椭圆的造型函数(Circular & Elliptical Shaping Functions): www.flong.com/archive/texts/code/shapers_circ
就像厨师自主选择辣椒和各种原料,数字艺术家和创意编程者往往钟情于使用他们自己的造型函数。
Iñigo Quiles 收集了一套有用的函数。在看过这篇文章后,看看下列函数转换到 GLSL 的样子。注意那些细小的改变,比如给浮点数(float)加小数点“.”,给“C 系函数”换成它们在 GLSL 里的名字,比如不是用
`powf()` 而是用 `pow()`:* Impulse
* Cubic Pulse
* Exponential Step
* Parabola
* Power Curve
给你们看些东西刺激一下斗志,这里有一个非常优雅的例子(作者是 Danguafer,造型函数的空手道黑带)。
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
在下一章我们会有一些新的进展。我们会先混合各种颜色,然后画些形状。
#### 练习
来看看 Kynd 帮大家制作的公式表。看他如何结合各种函数及它们的属性,始终控制值的范围在 0.0 到 1.0。好了,现在是你自己练习的时候了!来试试这些函数,记住:熟能生巧。
#### 填充你的工具箱
这里有一些工具可以帮你更轻松地可视化这些函数。
* GraphToy:仍然是 Iñigo Quilez 为大家做的工具,用于在 WebGL 中可视化 GLSL 函数。
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README De (05/README-de.md)
Algorithmisches Zeichnen
Modellierungsfunktionen
Dieses Kapitel könnte auch die Überschrift „Mr. Miyagi's Zaun- Lektion“ tragen, in Anspielung auf den Film „Karate Kid“ aus dem Jahre 1984. Bislang haben wir die normalisierte Position von x und y auf den roten und grünen Farbkanal des jeweiligen Punktes abgebildet. Wir haben dazu eine Funktion gebaut, die im Wesentlichen einen zweidimensionalen Vektor (X und Y) entgegennimmt und einen vierdimensionalen Vektor (Rot, Grün, Blau und Alpha) zurückliefert. Aber bevor wir damit fortfahren, Daten zwischen verschiedenen Dimensionen zu transformieren, müssen wir mit einfacheren Aufgaben beginnen. Und zwar mit viel einfacheren Aufgaben. Ich meine damit das Verständnis eindimensionaler Funktionen. Desto mehr Zeit und Energie Du darauf verwendest, desto stärker wird Dein Shader-Karate sein.
Die folgende Codestruktur ist unser Zaun, an dem wir in Anlehnung an die Geschichte von „Karate Kid“ üben werden. Wir visualisieren damit den normalisierten Wert der X-Ordinate (
`st.x`) auf zweierlei Weise: einmal in Form der Helligkeit (beachte den weichen, schrittweisen Übergang von Schwarz zu Weiß) und außerdem indem wir die Werte in Form einer grünen (Funktions-) Linie darstellen (der jeweilige X-Wert wird dabei eins-zu-eins auf den Y-Wert abgebildet). Halte Dich dabei bitte nicht zu lange mit der plot()-Funktion auf, denn darauf werden wir gleich noch im Detail eingehen.<div class="codeAndCanvas" data="linear.frag"></div>
Ein kurzer Hinweis: Der Konstruktor des
`vec3`-Datentyps „versteht“, wenn Du allen drei Farbkanälen durch die Angabe eines einzigen Parameters diesen einen gleichen Wert zuweisen willst. In diesem Fall erhält man immer einen Grauton zwischen Schwarz `(0.0, 0.0, 0.0)` und Weiß `(1.0, 1.0, 1.0)`.Ebenso versteht der Konstruktor des
`vec4`-Datentyps, wenn Du einen vierdimensionalen Vektor aus einem dreidimensionalen Vektor erzeugen willst. Der dreidimensionale Vektor verkörpert in diesem Fall den RGB-Farbwert, der zusätzliche Parameter den Wert für den Alpha-Kanal, also die Deckkraft. Dies geschieht hier in den Programmzeilen 19 und 25.Dieser Programmcode ist Dein Zaun, an dem Du das Streichen bzw. Malen üben kannst. Es ist wichtig, dass Du Dir den Code genau anschaust und ihn verstehst. Denn die darin enthaltenen Konzepte, wie etwa die Normalisierung von x und y auf Werte zwischen 0.0 und 1.0, werden uns weiterhin begleiten.
Die hier genutzte Eins-zu-Eins-Abbildung zwischen x und y (in diesem Fall als Helligkeit genutzt) wird als lineare Interpolation bezeichnet. Davon abweichend können wir auch unterschiedliche mathematische Funktionen nutzen, um der Linie eine andere Form zu geben. So können wir zum Beispiel x hoch 5 einsetzen, um eine geschwungene, steil ansteigende Kurve zu erzeugen.
<div class="codeAndCanvas" data="expo.frag"></div>
Interessant, nicht wahr? Versuche einfach einmal, in der Zeile 22 andere Exponenten wie beispielsweise
`20.0`, `2.0`, `1.0`, `0.0`, `0.2` und `0.02` einzusetzen. Das Verständnis zwischen dem Exponenten und den daraus resultierenden Werten wird Dir sicher weiterhelfen. Schließlich ist es der geschickte Einsatz dieser und anderer mathematischer Funktionen, die Dir bei der Shader-Programmierung unglaubliche Möglichkeiten eröffnen.`pow()` ist eine eingebaute mathematische Funktion von GLSL. Daneben gibt es noch viele weitere. Die meisten davon können sehr schnell in der Hardware der Grafikkarte ausgeführt werden. Ihr geschickter Einsatz beschleunigt deshalb die Ausführung Deiner Shader.Ersetze die Funktion pow in der Zeile 22 durch eine andere Funktion und beobachte das Resultat. Probiere zum Beispiel die folgenden Funktionen aus:
`exp()`, `log()` und `sqrt()`. Einige dieser Funktionen erzeugen interessantere Ergebnisse, wenn man sie in Verbindung mit einem Vielfachen oder einem Bruchteil der Kreiszahl pi nutzt. In der Programmzeile 8 findest Du deshalb ein Makro, dass das Symbol `PI` innerhalb des Programmcodes durch den zugehörigen Wert `3.14159265359` ersetzt.Step und Smoothstep
GLSL bringt auch einige einzigartige Interpolationsfunktionen mit, die durch die Hardware beschleunigt werden.
`step()` nimmt zwei Argumente entgegen: Das erste verkörpert den Schwellenwert, während das zweite Argument den Wert darstellt, der mit diesem Schwellenwert verglichen werden soll. Liegt dieser Wert unterhalb des Schwellenwerts, liefert die Funktion `0.0` zurück, bei jedem Wert größer oder gleich dem Schwellenwert hingegen `1.0`.Ändere im folgenden Programmcode doch einfach einmal den Schwellenwert in Zeile 20 und beobachte, was dann passiert.
<div class="codeAndCanvas" data="step.frag"></div>
Die zweite eingebaute Interpolationsfunktion trägt den Namen
`smoothstep()`. Sie interpoliert einen Wert, sofern sich dieser innerhalb eines angegebenen Wertbereichs befindet. Die ersten zwei Argumente stellen dabei die untere und die obere Schwelle dieses Wertebereichs dar, während das dritte Argument den zu interpolierenden Wert verkörpert.Die Funktion liefert
`0.0` zurück, wenn der zu interpolierende Wert unterhalb des genannten Schwellenwerts liegt, also kleiner als das erste Argument ist. Analog dazu liefert sie `1.0` zurück, wenn der zu interpolierende Wert größer als der obere Schwellenwert
ist. Befindet sich der zu interpolierende Wert jedoch innerhalb der gegebenen Spanne, wird ein Wert zwischen `0.0` und `1.0` zurückgeliefert, je nachdem wie nahe sich der Wert am oberen oder unteren Ende der Spanne befindet. Genau in der Mitte lautet das Ergebnis `0.5`.Wie das folgende Programm zeigt, ist das Ergebnis jedoch nicht vollständig linear, sondern am oberen und unteren Ende des Wertebereichs etwas „abgerundet“. Dadurch wird in den Randbereichen bewusst ein etwas weicherer Übergang erzielt, falls sich weitere interpolierte Werte anschließen.
<div class="codeAndCanvas" data="smoothstep.frag"></div>
Im obigen Beispiel nutzen wir die
`smoothstep()`-Funktion innerhalb der `plot()`-Funktion, um die grüne Linie zu erzeugen, die die Ergebnisse aus der Y-Berechnung in Zeile 20 darstellt. Siehst Du, wie diese grüne Linie nach oben und unten jeweils ein wenig ausfedert und sanft in den Hintergrund übergeht? Das erreichen wir, indem wir in Zeile 12 und 13 zwei Aufrufe von `smoothstep()` miteinander verbinden. Schau Dir die folgende Berechnung an und setze sie in die Zeile 20 ein.glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Die Formel entspricht dem Vorgehen innerhalb der
`plot()`-Funktion. Sie symbolisiert, wie `plot()` genau in der Mitte (auf dem gegebenen Y-Wert) ein volles Grün erzeugt (Rückgabewert `1.0`), während bei Werten darunter und darüber ein weicher Übergang zur Hintergrundfarbe erfolgt (Rückgabewerte von `1.0` langsam absteigend zu `0.0`).
Sinus und Cosinus
Wenn man mathematische Funktionen nutzen möchte, um Grafiken zu animieren, in eine geschwungene Form zu bringen oder sanft ein- und auszublenden, gibt es nichts Besseres, als sich mit Sinus und Cosinus anzufreunden.
Diese beiden trigonometrischen Funktionen sind so praktisch wie ein Schweizer Offiziersmesser, wenn es darum geht, Kreise zu konstruieren. Es ist wichtig zu verstehen, wie sich die beiden Funktionen einzeln verhalten und wie man sie gemeinsam nutzen kann. Kurz gesagt, bei einem gegebenen Winkel (in Form eines Bogenmaßes) liefern sie die korrekte x- (Cosinus) und y- (Sinus) Ordinate für die Position des zugehörigen Punktes auf einem Einheitskreis mit dem Radius
`1`. Weil die zurückgelieferten Funktionsergebnisse dabei immer dynamisch zwischen `-1.0` und `1.0` oszillieren, sind diese Funktionen ein ungeheuer praktisches Werkzeug für vielerlei Aufgaben.Es ist nicht ganz leicht, alle Zusammenhänge zwischen trigonometrischen Funktionen auf der einen Seite und Kreisen auf der anderen Seite zu beschreiben. Die obige Animation zeigt jedoch sehr schön die oben zitierte Rolle von Sinus und Cosinus bei der Erzeugung eines Einheitskreises.
<div class="simpleFunction" data="y = sin(x);"></div>
Schau Dir die Sinuswelle genau an und beobachte, wie der daraus abgeleitete Wert für die Y-Ordinate auf dem Einheitskreis sanft zwischen
`+1` und `-1` oszilliert. Und mit der Cosinuswelle erzeugen wir die zugehörige X-Ordinate.Wie wir in dem zeitbasierten Beispiel im vorangegangenen Kapitel gesehen haben, lässt sich dieses rhythmische Verhalten von
`sin()` gut nutzen, um bestimmte Werte und Eigenschaften zu animieren. Wenn Du diesen Text in einem Internet-Browser liest, wirst du feststellen, dass Du die obige Formel y=sin(x); editieren kannst, um zu sehen, wie sich die Funktionskurve dadurch ändert. (Hinweis: Bitte nicht das Semikolon am Ende der Zeile vergessen, sonst gibt es einen Syntaxfehler.)Probiere die folgenden Übungen aus und beobachte, was daraufhin geschieht:
Addiere die Zeit (
`u_time`) zu x hinzu, bevor Du aus der Summe den `sin` berechnest. Verinnerliche die daraus entstehende Bewegung entlang der x-Achse*. Multipliziere x* mit
`PI`, bevor Du den `sin` berechnest. Beobachte, wie sich die Phasenlänge auf jeweils 2 Einheiten entlang der X-Achse verkürzt und sich die Schwingung dann wiederholt. Dadurch verdoppelt sich also die Frequenz. Multipliziere die Zeit (
`u_time`) mit x*, bevor Du daraus den `sin` berechnest. Du wirst sehen, wie die einzelnen Wellen so weit zusammengedrückt werden, dass das Ergebnis wie ein unidentifizierbares Rauschen wirkt.* Addiere den Wert
`1.0` zum Ergebnis von `sin(x)` hinzu und beobachte, wie sich die Welle dadurch nach oben verschiebt und jeweils zwischen den Werten von `0.0` und `2.0` oszilliert.`sin(x)` mit `2.0`. Du wirst feststellen, dass sich die Amplitude der Schwingung (die Minimal- und Maximalwerte) verdoppelt.`abs()`) von `sin(x)` berechnen. Der entstehende Graph erinnert an die Spur eines hüpfenden Balls, nicht wahr?`fract()`) des Resultats von `sin(x)` berechnen.`ceil()`) von `sin(x)` und den kleineren Integer-Wert (`floor()`) von `sin(x)`, um eine digitale Welle zwischen `1` und `-1` zu erhalten.Einige besonders nützliche Funktionen
Am Ende der letzten Übung haben wir einige neue Funktionen eingeführt. Jetzt ist es an der Zeit, mit diesen Funktionen zu experimentieren, indem Du Schritt für Schritt die Kommentarzeichen aus den folgenden Programmzeilen entfernst. Lerne diese Funktionen kennen und beobachte, wie sie funktionieren. Du wirst Dich vielleicht fragen, warum? Eine Google-Suche nach „generativer Kunst“ bzw. „generative art“ wird Dir diese Frage schnell beantworten. Denke daran, dass diese Funktionen wie der Zaun aus „Karate Kid“ sind. Noch bewegen wir uns nur in einer Dimension, aufwärts und abwärts. Aber bald schon geht es damit in zwei, drei und vier Dimensionen!
<div class="simpleFunction" data="y = mod(x,0.5); // liefert den Modulo von x mit 0.5
//y = fract(x); // liefert den Nachkommateil einer Zahl
//y = ceil(x); // die kleinste Ganzzahl, groesser oder gleich x
//y = floor(x); // die naechste Ganzzahl, kleiner oder gleich x
//y = sign(x); // das Vorzeichen von x
//y = abs(x); // der absolute Wert von x
//y = clamp(x,0.0,1.0); // x auf den Bereich zwischen 0.0 und 1.0 abbilden
//y = min(0.0,x); // die kleinere Zahl von x und 0.0 zurueckliefern
//y = max(0.0,x); // die groessere Zahl von x und 0.0 zurueckliefern"></div>
Fortgeschrittene formgebende Funktionen
Golan Levin hat eine großartige Dokumentation über komplexe formgebende Funktionen verfasst, die für unsere Zwecke extrem hilfreich ist. Indem Du einen Teil dieser Funktionen nach GLSL portierst, schaffst Du Dir eine wertvolle Sammlung an Codeschnipseln.
* Polynomische formgebende Funktionen: www.flong.com/archive/texts/code/shapers_poly
* Exponentielle formgebende Funktionen: www.flong.com/archive/texts/code/shapers_exp
* Kreisförmige & elliptische formgebende Funktionen: www.flong.com/archive/texts/code/shapers_circ
* Bezier und andere parametrische formgebende Funktionen: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Genau wie Küchenchefs Gewürze und exotische Zutaten sammeln, entwickeln Digitalkünstler und kreative Entwickler häufig eine Liebe für bestimmte formgebende Funktionen.
Inigo Quiles stellt eine großartige Auswahl an nützlichen Funktionen vor. Wenn Du diesen Artikel gelesen hast, dann werfe einen Blick auf die folgenden Übertragungen dieser Funktionen nach GLSL. Beobachte aufmerksam die kleinen, aber erforderlichen Anpassungen wie etwa den Punkt (".") bei Fließkommazahlen und die Umsetzung der Funktionsnamen von C auf GLSL; so heißt es in GLSL beispielsweise
`pow()` statt `powf()` wie in C: <div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Um Deine Motivation zu erhalten, hier ein elegantes Beispiel (entwickelt von Danguafer) für die Meisterschaft im Karate der formgebenden Funktionen.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
Im nächsten Kapitel werden wir neue Schrittfolgen für unser „Karate“ lernen. Zunächst beim Mischen von Farben, dann beim Malen von Formen.
####Übung
Wirf einen Blick auf die folgende Tabelle mit Gleichungen, die von Kynd erstellt wurde. Schau Dir an, wie er Funktionen und ihre Eigenschaften kombiniert, um Werte zwischen
`0.0` und `1.0` zu erhalten. Jetzt ist ein guter Moment, um mit diesen Funktionen ein wenig zu experimentieren. Denn je mehr Du in dieses Denken hineinwächst, desto besser wird Dein Karate sein.####Für Deine Werkzeugsammlung
Hier kommen einige Tools, die es Dir erleichtern werden, diese Art von Funktionen grafisch zu visualisieren.
* GraphToy: Einmal mehr ein Werkzeug von Inigo Quilez, mit dem man GLSL-Funktionen in WebGL darstellen kann.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README Es (05/README-es.md)
Dibujando con algoritmos
Funciones de forma
Este capítulo se podría llamar "La lección de la cerca del Sr Miyagi". Anteriormente normalizamos la posición de x e y al canal de rojo y verde. Esencialmente creamos una función que tomaba dos vectores dimensionales (x e y) y devolvía un vector de cuatro dimensiones (rojo, verde, azul y transparencia). Pero antes de ir más lejos, transformando valores entre dimensiones, necesitamos comenzar con algo sencillo... mucho más sencillo. Eso significa comprender las funciones unidimensionales. A mayor tiempo y energía que pongas en aprender esto, mejor será tu karate.
El siguiente código va a ser nuestra cerca. Dentro del código visualizamos el valor normalizado de la coordenada x (
`st.x`) de dos maneras: una visualizando el brillo (observa el bonito gradiente de fondo del negro al blanco) y otra dibujando una línea verde arriba (en este caso el valor de x es asignado directamente al valor de y). No te enfoques mucho en la función plot, la veremos en detalle en unos momentos.<div class="codeAndCanvas" data="linear.frag"></div>
Nota rápida: el constructor de
`vec3` "entiende" que quieres asignar tres colores con la misma variable, mientras que el `vec4` entiende que quieres crear un vector tridimensional con un cuarto valor (en este caso el que controla el alpha). Mira por ejemplo la línea 19 y la 25 de arriba.Este código es tu cerca; es importante observarlo y entenderlo. Volverás aquí una y otra vez, a este espacio entre el 0.0 y el 1.0. Aprenderás el arte de doblar y dar forma a esta línea.
Esta relación par entre x e y (o el brillo) es conocida como interpolación lineal. Desde aquí podemos utilizar funciones matemáticas para darle forma a esta línea. Por ejemplo podemos elevar x a la quinta potencia para generar una línea curva.
<div class="codeAndCanvas" data="expo.frag"></div>
Interesante ¿No? En la línea 22 puedes probar diferentes exponentes: 20.0, 2.0, 1.0, 0.0, 0.2 o 0.02 por ejemplo. Entender esta relación entre el valor y el exponente nos será muy útil. Usar este tipo de funciones matemáticas aquí y allá nos dará un control expresivo sobre nuestro código, como si fuese un tipo de acupuntura con el que manejas el flujo de los valores.
`pow()` es una función nativa en GLSL y hay muchas más. La mayoría de ellas son aceleradas por hardware, lo que significa que, usadas de la forma correcta, harán tu código mucho más rápido.`exp()`, `log()` y `sqrt()`. Algunas de estas funciones son mucho más interesantes cuando las usamos con PI. En la línea 8 definí un macro que reemplaza cualquier llamado a `PI` por el valor `3.14159265359`.Step y Smoothstep
GLSL también cuenta con algunas funciones únicas de interpolación, que también son aceleradas por hardware.
`step()` recibe dos parámetros. El primero es el límite o umbral, el segundo es el valor que queremos chequear. Cualquier número por debajo del límite devuelve `0.0` todo lo que lo supere devuelve `1.0`.Intenta cambiar el límite en la línea 20 del siguiente código.
<div class="codeAndCanvas" data="step.frag"></div>
`smoothstep()`. Dado un rango de dos números y un valor, esta función interpola el valor entre el rango definido. Los primeros dos parámetros son para el comienzo y el final de la transición, el tercero es el valor a interpolar.<div class="codeAndCanvas" data="smoothstep.frag"></div>
En el anterior ejemplo, en la línea 12, hemos usado smoothstep para dibujar una línea verde en la función
`plot()`. Por cada posición en el eje x esta función crea una salto en un valor particular de y. ¿Cómo? Conectando dos `smoothstep()` juntos. Observa la siguiente función y reemplaza la línea 20 por esta, y piensa en ella como un corte vertical. El fondo se parece a una línea ¿No?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Seno y Coseno
Cuando queremos usar un poco de matemática para animar, dar forma o mezclar valores, no hay nada mejor que ser amigos del seno y del coseno.
Estas dos funciones básicas trigonométricas trabajan juntas creando círculos y son más útiles que la navaja suiza de MacGyver. Es importante saber como se comportan y de que forma pueden ser combinadas. En pocas palabras, dado un ángulo (en radianes) devolverán la posición de x (coseno) e y (seno) de un punto en el borde de un círculo con un radio igual a 1. Como estas funciones devuelven un valor normalizado (entre -1 y 1) y suavizado, terminan siendo una herramienta increíble.
Como es difícil describir la relación entre las funciones trigonométricas y los círculos, la hermosa animación de arriba hace el trabajo de explicarlo visualmente.
<div class="simpleFunction" data="y = sin(x);"></div>
Presta mucha atención a esta curva sinusoidal. Nota como los valores de Y fluyen suavemente entre -1 y 1. Como vimos en el anterior capítulo, podemos utilizar el comportamiento rítmico de
`sin()` para animar propiedades. Si estás leyendo este ejemplo en un navegador, puedes modificar la fórmula que aparece aquí arriba y ver cómo cambia la onda. (Nota: No olvidar el punto y coma al final.)Completa los siguientes ejercicios y presta atención a lo que sucede:
Suma el tiempo (
`u_time`) a x antes de computar el `sin`. Observa detenidamente ese movimiento a lo largo de x*. Multiplica x* por
`PI` antes de computar el `sin`. Nota como las dos fases se encogen y cada ciclo se repite dos veces. Multiplica el tiempo (
`u_time`) por x* antes de computar el `sin`. Observa como la frecuencia entre las fases se comprime más y más. Nota que u_time en un momento pasa a ser un número muy alto y se hace difícil ver el gráfico.`sin(x)`. Observa como toda la onda es desplazada hacia arriba y ahora todos los valores van de 0.0 a 2.0.`sin(x)` por 2.0. Mira como la amplitud duplica su tamaño.`abs()`) de `sin(x)`. Observa como se parece a la trayectoria de una pelota rebotando.`fract()`) del resultado de `sin(x)`.`ceil()`) y el menor entero más cercano (`floor()`) del resultado de `sin(x)` para obtener la onda digital de 1.0 y -1.0.Otras funciones útiles
Al final del último ejercicio hemos introducido algunas funciones nuevas. Ahora es el momento de experimentar con cada una descomenteando las siguientes lineas, de a una. Es importante entender estas funciones y estudiar como se comportan. Ya lo sé ¿Para qué? Si buscas rápidamente en Google "Arte Generativo" vas a entender el por qué. Ten en cuenta que estas funciones son nuestra cerca. Estamos dominando el movimiento en una sola dimensión, arriba y abajo. ¡Pronto, será el momento de agregar la segunda, la tercera y la cuarta dimensión!
<div class="simpleFunction" data="y = mod(x,0.5); // return x modulo of 0.5
//y = fract(x); // return only the fraction part of a number
//y = ceil(x); // nearest integer that is greater than or equal to x
//y = floor(x); // nearest integer less than or equal to x
//y = sign(x); // extract the sign of x
//y = abs(x); // return the absolute value of x
//y = clamp(x,0.0,1.0); // constrain x to lie between 0.0 and 1.0
//y = min(0.0,x); // return the lesser of x and 0.0
//y = max(0.0,x); // return the greater of x and 0.0 "></div>
Funciones de forma avanzadas
Golan Levin tiene en su página documentación muy útil sobre cómo generar formas complejas con funciones. Trasladar estas funciones a GLSL es una muy buena forma de comenzar a generar nuestras propias piezas de código.
* Polynomial Shaping Functions: www.flong.com/archive/texts/code/shapers_poly
* Exponential Shaping Functions: www.flong.com/archive/texts/code/shapers_exp
* Circular & Elliptical Shaping Functions: www.flong.com/archive/texts/code/shapers_circ
* Bezier and Other Parametric Shaping Functions: www.flong.com/archive/texts/code/shapers_bez
Como los chefs que colectan especias e ingredientes exóticos, los artistas digitales y creative coders tienen un amor particular por crear sus propias funciones de dibujo.
Iñigo Quiles tiene una gran colección de funciones útiles. Después de leer este artículo echa un vistazo a la traducción de esas funciones a GLSL. Presta atención a los pequeños cambios necesarios, como poner "." (punto) en los valores flotantes y usar los nombres en GLSL de las funciones en C; por ejemplo en vez de
`powf()` usamos `pow()`:* Impulse
* Cubic Pulse
* Exponential Step
* Parabola
* Power Curve
Para que te mantengas motivado, aqui hay un elegante ejemplo (hecho por Danguafer) de alguien que logró dominar su karate en las funciones.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
En el Siguiente >> capítulo comenzaremos a usar nuevos movimientos. Primero mezclaremos color y luego dibujaremos formas.
#### Ejercicio
Presta atención a la siguiente tabla de ecuaciones hecha por Kynd. Observa como combina las funciones y sus propiedades para controlar los valores de 0.0 a 1.0. Ahora es el momento de practicar replicando estas funciones. Recuerda que cuanto más practiques esto, mejor será tu karate.
#### Para tu caja de herramientas
Estas son algunas herramientas que te ayudarán a visualizar este tipo de funciones.
* GraphToy: una vez más Iñigo Quilez ha creado una herramienta para visualizar funciones GLSL en WebGL.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README Fr (05/README-fr.md)
Dessin Algorithmique
Fonctions de formes
Ce chapitre aurait pu s'appeler "la palissade de monsieur Miyagi" (pour ceux qui n'étaient pas nés en 1984)).
Au chapitre précédent, nous avons mappé les coordonnées x et y normalisées sur les canaux rouge et vert.
Nous avons créé une fonction qui prend un vecteur à deux dimensions (x et y) et retourne un vecteur à quatre dimensions (r, g, b et a).
Avant de transformer des données à plusieurs dimensions, il nous faut commencer par des choses simples... beaucoup plus simples.
Concrètement, nous devons comprendre commment marchent les fonctions à une dimension.
Plus vous passerez de temps à apprendre et à maîtriser ces fonctions, plus votre Karaté-Shader sera redoutable.
Le code suivant sera notre palissade.
Sur cette palissade, nous visualisons la valeur normalisée de la position x (
st.x) de deux façons : la luminosité (c'est le joli dégradé du noir au blanc en arrière plan) et une ligne verte dessinée par dessus (dans ce cas, la valeur x est assignée directement à y).
Ne vous focalisez pas trop sur la fonction plot pour l'instant, nous y reviendrons en détail dans un moment.<div class="codeAndCanvas" data="linear.frag"></div>
Note: Le constructeur du type
vec3, un vecteur à 3 dimensions (r,g,b ou x,y,z ; c'est la même chose), "comprend" que vous allez le construire avec la même valeur pour les trois canaux / dimensions.
Il est donc possible d'ecrire vec3 color = vec3(y); pour construire un vecteur à 3 dimensions. Ce vecteur aura la valeur y assignée à chaque canal / dimension soit : color.x = color.y = color.z = y ou color.r = color.g = color.b = y (puisqu'on peut accéder aux variables du vecteur des 2 manières).
Le constructeur du type vec4 en revanche, "comprend" que vous allez le construire, soit en passant quatre valeurs dinstinctes : vec4(0., 0.5, 1., 1.), soit en lui passant un vec3 et un float (un nombre) : vec4(color, 1.).
Dans notre cas, la valeur du second paramètre (le float), permet de gérer l'opacité aussi appelé l'alpha.
Référez vous aux lignes 19 et 25 dans l'exemple ci-dessus pour bien voir la différence de construction des 2 types.Ce code sera votre palissade ; il est important de bien l'observer et de bien le comprendre.
Vous reviendrez souvent dans cet espace entre 0.0 et 1.0 pour maîtriser l'art de le transformer et de sculpter cette ligne.
La relation entre x et y (la luminosité du dégradé), s'appelle une interpolation.
Comme on passe une seule valeur (y) à la variable color, on obtient un niveau de gris et comme la valeur de x est normalisée, elle est comprise entre 0.0 et 1.0, on obtient un dégradé du noir (x=0.) au blanc (x=1.).
L'interpolation est un principe fondamental ; elle nous permet de faire passer progressivement une valeur de A vers B en fonction d'une troisième valeur T normalisée entre 0.0 et 1.0.
La ligne verte reflète ce qui se passe lors de l'interpolation, en l'occurrence, c'est une ligne droite puisque x passe de 0.0 à 1.0 de façon linéaire (x va de 0.0 à 1.0 de façon continue).
A partir de là, nous pouvons utiliser des fonctions mathématiques pour sculpter la ligne. Par exemple, on peut passer x à la puissance 5 pour créer une ligne courbe.
<div class="codeAndCanvas" data="expo.frag"></div>
Intéressant n'est-ce-pas ? A la ligne 19, au lieu de
5.0 essayez différents exposants, par exemple: 20.0, 2.0, 1.0, 0.0, 0.2 et 0.02.
Comprendre la relation qui existe entre la valeur et l'exposant va nous être très utile.
Ce genre de fonction mathématique nous donne un contrôle sur l'expressivité du code, c'est une sorte d'acupuncture qui permet de contrôle les flux de valeurs.pow() est une fonction native de GLSL et il y en a de nombreuses autres.
La plupart sont accélérées matériellement, ce qui signifie que si on les utilise bien et avec parcimonie, elle permettent au code de s'exécuter plus vite.Essayez de remplacer la fonction
pow() ligne 19 par: exp(), log() ou sqrt().
Certaines de ces fonctions deviennent vraiment intéressantes quand on les utilise avec PI. Vous pouvez voir que j'ai déclaré une macro qui remplacera chaque appel à PI par la valeur 3.14159265359.
Par exemple : sin(st.x PI), produira une parabole, pow(sin(st.x PI), 5.) effectuera un pincement de la parabole.Step et Smoothstep
GLSL propose également des fonctions d'interpolation natives et accélérées matériellement.
step() prend 2 paramètres, le premier est une limite ou un seuil et le second est la valeur à tester.
Toute valeur inférieure au seuil renverra 0.0 tandis que toute valeur supérieure au seuil renverra 1.0.Essayez de changer la valeur de seuil à la ligne 20 du code suivant :
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep().
Avec deux nombres A et B et une valeur d'interpolation T comprise entre 0.0 et 1.0, elle nous permet de passer progressivement (et en souplesse) de A à B en fonction de T.
Les deux premiers arguments sont les bornes (A et B) et le troisième est la valeur d'interpolation (T).<div class="codeAndCanvas" data="smoothstep.frag"></div>
Dans l'exemple ci-dessus, ligne 12, vous remarquez qu'on a utilisé
smoothstep() depuis le début dans la fonction plot() qui permet de dessiner la ligne verte.
Pour chaque position le long de l'axe des x, cette fonction crée une bosse à un endroit précis de l'axe des y.
Comment ? en combinant deux appels à smoothstep().
Remplacez la ligne 20 par la fonction suivante et regardez l'arrière plan, on dirait une ligne floutée non ?
En rapprochant les valeurs de seuil des smoothstep() (0.45, 0.5 et 0.5, 0.55 par exemple), la ligne devient plus fine et moins floue.glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sinus et Cosinus
Le mieux, si vous voulez utiliser les maths pour animer ou passer d'une valeur à une autre, c'est d'être copain avec les sinus et cosinus.
Ces deux fonctions trigonométriques combinées permettent de dessiner des cercles qui sont plus utiles que le couteau suisse de MacGyver.
Il est important de comprendre ce qu'elles font et comment les combiner.
Dans un mouchoir de poche, à partir d'un angle en radians (1 radian = PI / 180), le cosinus renvoie la position x et le sinus renvoie la position y d'un point sur un cercle de rayon 1.
Le fait que ces fonctions renvoient des valeurs normalisées (entre -1 et 1) de manière très "continue" ou très "souple", en fait un outil indispensable.
S'il est difficile de comprendre cette relation entre sinus / cosinus et le cercle unitaire (de rayon 1), l'animation ci-dessus la résume assez bien visuellement.
<div class="simpleFunction" data="y = sin(x);"></div>
Regardez attentivement cette sinusoïde et surtout comment la valeur y oscille entre -1 et 1.
Comme nous l'avons vu sur l'exemple du temps au chapitre 3, on peut utiliser cette propriété temporelle de
sin() pour animer une valeur.
Si vous regardez cet exemple dans un navigateur, vous pouvez changer le code de la formule ci-dessus pour voir comment la sinusoïde change (n'oubliez pas le point-virule en fin de ligne).Essayez les changements suivants et regardez ce qui se passe :
Ajoutez le temps (
u_time) à x avant de calculer sin. Retenez ce mouvement le long des x*. Multipliez x par
PI avant de calculer sin. remarquez comme les deux phases* rétrécissent de manière à ce que chaque cycle (tour complet) se répète entre deux valeurs entières. Multipliez le temps (
u_time) par x* avant de calculer sin. remarquez comme la fréquence entre phases se compresse. Notez qu'u_time peut déjà avoir une valeur très élevée, rendant le graphe illisible.sin(x). Notez que toutes les vagues se déplacent vers le haut et comme toutes les valeurs sont maintenant comprises entre 0 et 2.sin(x) par 2.0 et notez comme l'amplitude doubles de taille.abs()) de sin(x), on dirait que le tracé rebondit.* Utilisez la partie fractionnelle (uniquement les chiffres après la virgule) (
fract()) du résultat de sin(x).ceil()) à l'entier le plus petit (floor()) du résultat de sin(x) pour obtenir des créneaux* entre -1 et 1.Quelques fonctions indispensables
A la fin de l'exercice précédent, nous avons présenté quelques nouvelles fonctions, nous allons maintenant les tester.
Pour ce faire, décommentez une ligne à la fois dans la liste suivante.
Essayez de comprendre comment chacune fonctionne et comment vous pourriez les combiner.
Vous vous demandez sans doute mais... pourquoi ?
Une petite recherche de "generative art" sur Google vous apportera la réponse.
Gardez à l'esprit que ces fonctions sont votre palissade, nous apprenons à maîtriser le mouvement sur une dimension, de bas en haut, puis de haut en bas.
Bientôt, nous utiliserons, deux, trois et même quatre dimensions!
<div class="simpleFunction" data="y = mod(x,0.5); // renvoie x modulo 0.5
//y = fract(x); // renvoie uniquement la partie fractionnelle d'un chiffre (les chiffres après la virgule)
//y = ceil(x); // renvoie le plus proche entier supérieur ou égal à x
//y = floor(x); // renvoie le plus proche entier inférieur ou égal à x
//y = sign(x); // renvoie le signe de x (-1 ou 1)
//y = abs(x); // renvoie la valeur absolue de x
//y = clamp(x,0.0,1.0); // force x à se retrouver entre 0.0 et 1.0
//y = min(0.0,x); // renvoie le plus petit chiffre entre 0 et x
//y = max(0.0,x); // renvoie le plus grand chiffre entre 0 et x"></div>
Fonctions de formes avancées
Golan Levin a écrit quelques articles très instructifs sur des fonctions plus complexes.
Les porter en GLSL est une bonne idée si vous souhaitez construire votre boîte à outils.
* Fonctions Polynomiales : www.flong.com/archive/texts/code/shapers_poly
* Fonctions Exponentielles : www.flong.com/archive/texts/code/shapers_exp
* Fonctions Circulaires & Elliptiques : www.flong.com/archive/texts/code/shapers_circ
* Fonctions de Bezier et autres fonctions paramétriques : www.flong.com/archive/texts/code/shapers_bez
Comme un chef qui collectionnerait les épices et autres ingrédients exotiques, les artistes digitaux et les codeurs créatifs en particulier aiment travailler leurs propres fonctions de forme.
Iñigo Quiles a écrit une liste de fonctions utiles.
Après avoir lu cet article regardez leur traduction en GLSL.
Notez bien les petits changements nécessaires, comme le "." (point) sur les floats et l'utilisation des conventions de nommage GLSL pour les fonctions C ; par exemple
powf() devient pow():* Impulse
* Cubic Pulse
* Exponential Step
* Parabola
* Power Curve
Pour vous motiver, voici un exemple élégant, fait par Danguafer qui montre le karaté des fonctions de formes.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
Au prochain chapitre, nous utiliserons ces nouveaux mouvements. D'abord pour mélanger des couleurs, puis pour dessiner des formes.
#### Exercices
Regardez cette table d'équations, réalisée par Kynd.
Voyez comme il combine les fonctions et leurs propriétés pour contrôler les variations de valuer entre 0 et 1.
A présent, c'est à votre tour de vous entraîner à reproduire ces fonctions.
Souvenez vous que plus vous pratiquerez, meilleur sera votre karaté.
#### Pour la boîte à outils
Voici quelques outils qui simplifient la visualisation des fonctions.
* GraphToy : Iñigo Quilez, encore lui, a créé un outil pour visualiser les fonctions GLSL en WebGL.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README Id (05/README-id.md)
Menggambar Secara Algoritmik
Shaping functions
Bab ini harusnya dinamakan "Pelajaran pagar Pak Miyagi". Sebelumnya, kita memetakan posisi dari x dan y yang dinormaliasasikan ke saluran merah dan hijau. Intinya kita membuat sebuah fungsi yang mengambil vektor dua dimensi (x dan y) dan mengembalikan vektor empat dimensi (r, g, b dan a). Tapi sebelum kita melangkah lebih jauh mentransformasikan data antar dimensi, kita harus mulai dari yang sederhana... jauh lebih sederhana. Maksudnya memahami bagaiman membuat fungsi satu dimensi. Semakin banyak energi dan waktu yang anda habiskan untuk mempelajari dan menguasainya, semakin kuat karate shader Anda.
Struktur kode berikut ini akan menjadi pagar kita. Di dalamnya, kami memvisualisasikan nilai yang dinormalisasi dari koordinat x (
st.x) dalam dua cara: satu dengan kecerahan (amati gradien yang bagus dari hitam ke putih) dan yang lainnya dengan memplot garis hijau di atas (dalam dalam hal ini nilai diberikan langsung ke y). Jangan terlalu fokus pada fungsi plot, kita akan membahasnya lebih detail sebentar lagi.<div class="codeAndCanvas" data="linear.frag"></div>
Catatan Singkat: Konstruktor jenis
vec3" memahami "bahwa Anda ingin menetapkan tiga saluran warna dengan nilai yang sama, sementara vec4 memahami bahwa Anda ingin membuat vektor empat dimensi dengan satu plus tiga dimensi nilai keempat (dalam hal ini nilai yang mengontrol alpha atau opacity). Lihat misalnya baris 19 dan 25 di atas.Kode ini adalah pagar Anda; penting untuk mengamati dan memahaminya. Anda akan kembali lagi dan lagi ke ruang ini antara 0,0 dan 1,0. Anda akan menguasai seni memadukan dan membentuk garis ini.
Hubungan satu-ke-satu antara x dan y (atau kecerahan) dikenal sebagai interpolasi linier. Dari sini kita dapat menggunakan beberapa fungsi matematika untuk bentuk (shape) garis. Misalnya kita dapat menaikkan x menjadi pangkat 5 untuk membuat garis melengkung .
<div class="codeAndCanvas" data="expo.frag"></div>
Menarik bukan? Pada baris 22 coba eksponen berbeda: 20.0, 2.0, 1.0, 0.0, 0.2 dan 0.02 misalnya. Memahami hubungan antara nilai dan eksponen ini akan sangat membantu. Menggunakan jenis fungsi matematika ini di sana-sini akan memberi Anda kontrol ekspresif atas kode Anda, semacam akupunktur data yang memungkinkan Anda mengontrol aliran nilai.
pow() adalah fungsi asli di GLSL dan masih banyak lagi lainnya. Kebanyakan dari mereka diakselerasi pada tingkat perangkat keras, yang berarti jika mereka digunakan dengan cara yang benar dan dengan kebijaksanaan mereka akan membuat kode Anda lebih cepat.exp(), log() dan sqrt(). Beberapa dari fungsi ini lebih menarik saat Anda bermain dengannya menggunakan PI. Anda dapat melihat pada baris 8 bahwa saya telah menetapkan makro yang akan menggantikan penggunaan PI dengan nilai 3.14159265359.Step dan Smoothstep
GLSL juga memiliki beberapa fungsi interpolasi asli unik yang diakselerasi dengan perangkat keras.
step() menerima dua parameter. Yang pertama adalah batas atau ambang, sedangkan yang kedua adalah nilai yang ingin kita periksa atau lewati. Setiap nilai di bawah batas akan mengembalikan 0.0 sementara semua yang di atas batas akan mengembalikan 1.0.Coba ubah nilai ambang batas ini pada baris 20 kode berikut.
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep(). Diberikan rentang dua angka dan nilai, fungsi ini akan menginterpolasi nilai antara rentang yang ditentukan. Dua parameter pertama adalah untuk awal dan akhir transisi, sedangkan yang ketiga adalah untuk nilai yang akan diinterpolasi.<div class="codeAndCanvas" data="smoothstep.frag"></div>
Pada contoh sebelumnya, pada baris 12, perhatikan bahwa kita telah menggunakan smoothstep untuk menggambar garis hijau pada fungsi
plot(). Untuk setiap posisi sepanjang sumbu x fungsi ini membuat lonjakan pada nilai y tertentu. Bagaimana? Dengan menghubungkan dua smoothstep() bersama-sama. Perhatikan fungsi berikut, ganti untuk baris 20 di atas dan anggap sebagai potongan vertikal. Latar belakangnya terlihat seperti garis, bukan?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sinus dan Kosinus
Saat Anda ingin menggunakan beberapa matematika untuk menganimasikan, membentuk, atau mencampurkan nilai, tidak ada yang lebih baik daripada berkawan dengan sinus dan cosinus.
Kedua fungsi trigonometri dasar ini bekerja sama untuk membuat lingkaran yang berguna seperti pisau tentara Swiss milik MacGyver. Penting untuk mengetahui bagaimana mereka berperilaku dan dengan cara apa mereka dapat digabungkan. Singkatnya, diberi sudut (dalam radian) mereka akan mengembalikan posisi yang benar dari x (cosinus) dan y (sinus) dari sebuah titik di tepi lingkaran dengan jari-jari yang sama dengan 1. Tapi, fakta bahwa mereka kembali atau
Meskipun sulit untuk mendeskripsikan semua hubungan antara fungsi trigonometri dan lingkaran, animasi di atas melakukan pekerjaan yang bagus dalam merangkum hubungan ini secara visual.
<div class="simpleFunction" data="y = sin(x);"></div>
Perhatikan baik-baik gelombang sinus ini. Perhatikan bagaimana nilai y mengalir dengan mulus antara +1 dan -1. Seperti yang kita lihat pada contoh waktu di bab sebelumnya, Anda dapat menggunakan perilaku ritmis
sin() untuk menghidupkan properti. Jika Anda membaca contoh ini di browser, Anda akan melihat bahwa Anda dapat mengubah kode dalam rumus di atas untuk melihat bagaimana wave berubah. (Catatan: jangan lupa titik koma di akhir baris.)Cobalah latihan berikut dan perhatikan apa yang terjadi:
Tambahkan waktu (
u_time) ke x sebelum menghitung sin. Internalisasikan gerakan di sepanjang x*. Kalikan x* dengan
PI sebelum menghitung sin. Perhatikan bagaimana kedua fase menyusut sehingga setiap siklus berulang setiap 2 bilangan bulat. Kalikan waktu (
u_time) dengan x* sebelum menghitung sin. Lihat bagaimana frekuensi antar fase menjadi semakin terkompresi. Perhatikan bahwa u_time mungkin sudah menjadi sangat besar, membuat grafik sulit dibaca.sin(x). Lihat bagaimana semua gelombang dipindahkan ke atas dan sekarang semua nilai berada di antara 0,0 dan 2,0.sin (x) dengan 2.0. Lihat bagaimana amplitudo berlipat ganda.abs()) dari sin(x). Ini terlihat seperti jejak bola yang memantul.fract()) dari resultan sin(x).ceil()) dan bilangan bulat yang lebih kecil (floor()) dari resultan dari sin (x) untuk mendapatkan gelombang digital nilai 1 dan -1.Beberapa fungsi yang berguna ekstra
Di akhir latihan terakhir kami memperkenalkan beberapa fungsi baru. Sekarang saatnya untuk bereksperimen dengan masing-masing dengan menghapus komentar baris di bawah satu per satu. Kenali fungsi-fungsi ini dan pelajari bagaimana perilakunya. Saya tahu, Anda bertanya-tanya ... mengapa? Pencarian Google cepat tentang "seni generatif" akan memberi tahu Anda. Ingatlah bahwa fungsi-fungsi ini adalah pagar kami. Kami menguasai gerakan dalam satu dimensi, naik turun. Sebentar lagi, waktunya untuk dua, tiga, dan empat dimensi!
<div class="simpleFunction" data="y = mod(x,0.5); // return x modulo of 0.5
//y = fract(x); // return only the fraction part of a number
//y = ceil(x); // nearest integer that is greater than or equal to x
//y = floor(x); // nearest integer less than or equal to x
//y = sign(x); // extract the sign of x
//y = abs(x); // return the absolute value of x
//y = clamp(x,0.0,1.0); // constrain x to lie between 0.0 and 1.0
//y = min(0.0,x); // return the lesser of x and 0.0
//y = max(0.0,x); // return the greater of x and 0.0 "></div>
Fungsi membentuk lanjutan
Golan Levin memiliki dokumentasi hebat tentang fungsi pembentukan yang lebih kompleks yang sangat membantu. Mem-portingnya ke GLSL adalah langkah yang sangat cerdas, untuk mulai membangun resource cuplikan kode Anda sendiri.
* Funsi Pembentukan polinomial: www.flong.com/archive/texts/code/shapers_poly
* Fungsi Pembentuan eksponensial: www.flong.com/archive/texts/code/shapers_exp
* Fungsi Pembentukan Lingkaran & Elips: www.flong.com/archive/texts/code/shapers_circ
* Bezier dan Fungsi Pembentukan Parametrik lainnya: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Seperti koki yang mengumpulkan rempah-rempah dan bahan-bahan eksotis, seniman digital dan pembuat kode kreatif sangat menyukai mengerjakan fungsi pembentukan mereka sendiri.
[Iñigo Quiles] (http://www.iquilezles.org/) memiliki banyak koleksi fungsi yang berguna. Setelah membaca artikel ini (http://www.iquilezles.org/www/articles/functions/functions.htm) lihat terjemahan berikut dari fungsi-fungsi ini ke GLSL. Perhatikan perubahan kecil yang diperlukan, seperti meletakkan "." (titik) pada bilangan floating point dan menggunakan nama GLSL untuk fungsi C; misalnya alih-alih
powf() gunakan pow():<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Untuk menjaga motivasi Anda tetap tinggi, berikut adalah contoh elegan (dibuat oleh [Danguafer) dalam menguasai karate fungsi-pembentukan.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
Di bab berikutnya kami akan mulai menggunakan gerakan baru kami. Pertama dengan mencampurkan warna dan kemudian menggambar bentuk.
#### Latihan
Lihatlah tabel persamaan yang dibuat oleh Kynd. Lihat bagaimana dia menggabungkan fungsi dan propertinya untuk mengontrol nilai antara 0,0 dan 1,0. Sekarang saatnya Anda berlatih dengan mereplikasi fungsi-fungsi ini. Ingat, semakin banyak Anda berlatih semakin baik karate Anda nantinya.
#### Untuk kotak peralatan Anda
Berikut beberapa alat yang akan memudahkan Anda untuk memvisualisasikan jenis fungsi tersebut.
* GraphToy: sekali lagiIñigo Quilez membuat alat untuk memvisualisasikan funsi GLSL dalam WebGL.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README It (05/README-it.md)
Disegno Algoritmico
Funzioni di forma
Questo capitolo potrebbe essere chiamato "la lezione del recinto di Mr. Miyagi". In precedenza abbiamo associato la posizione normalizzata di x e y ai canali rosso e verde. Fondamentalmente abbiamo creato una funzione che prende un vettore bidimensionale (x e y) e restituisce un vettore quadridimensionale (r, g, b e a). Ma prima di andare avanti nella trasformazione dei dati tra dimensioni, dobbiamo iniziare da qualcosa di più semplice…molto più semplice. Questo significa comprendere come creare funzioni unidimensionali. Maggiori sono l’energia e il tempo che impieghi nel comprendere e nel padroneggiarle, migliore sarà il tuo karate shader.
La seguente struttura di codice sarà la nostra recinzione. In questa visualizziamo il valore normalizzato della coordinata x (
`st.x`) in due modi: uno con la luminosità (osserva il bel gradiente dal nero al bianco) e l’altro tracciando sopra una linea verde (in questo caso il valore di x è assegnato direttamente al valore di y). Non ti concentrare troppo sulla funzione plot, la vedremo nel dettaglio tra poco.<div class="codeAndCanvas" data="linear.frag"></div>
Nota veloce: il costruttore di tipo
`vec3` "capisce" che vuoi assegnare i tre canali di colori allo stesso valore, mentre `vec4` capisce che vuoi costruire un vettore a quattro dimensioni con tre unidimensionali più un quarto valore (in questo caso il valore che controlla l’alfa o l’opacità). Guarda, ad esempio, le righe 19 e 25 qui sopra.Questo codice è il tuo recinto; è importante osservarlo e capirlo. Tornerai spesso in questo spazio tra 0.0 e 1.0. Imparerai l’arte di combinare e modellare questa linea.
Questa relazione uno a uno tra x e y (o la luminosità) è conosciuta come interpolazione lineare. A partire da qui, possiamo usare alcune funzioni matematiche per modellare la linea. Per esempio, possiamo elevare x alla quinta potenza e creare una linea curva.
<div class="codeAndCanvas" data="expo.frag"></div>
Interessante, vero? Alla riga 22 prova esponenti diversi, per esempio: 20.0, 2.0, 1.0, 0.0, 0.2 e 0.02. comprendere questa relazione tra il valore e l’esponente sarà molto utile. Questo genere di funzioni matematiche ci darà un controllo espressivo sul codice, come una specie di agopuntura che permette di controllare il flusso dei valori.
`pow()` è una funzione nativa il GLSL e ce ne sono molte altre. La maggior parte di queste sono accelerate al livello dell’hardware; ciò significa che se esse sono usate in modo appropriato e con discrezione, renderanno il tuo codice molto più veloce.`exp()`, `log()` e `sqrt()`. Alcune di queste funzioni sono più interessanti quando le si utilizza con PI. Puoi vedere alla riga 8, che ho definito un macro che sostituisce qualsiasi chiamata a `PI` con valore `3.14159265359`.Step e Smoothstep
GLSL ha anche alcune funzioni interpolanti native uniche che sono accelerate dall’hardware.
`step()` riceve due parametri. Il primo è il limite o la soglia, mentre il secondo è il valore che volgiamo controllare. Qualsiasi valore al di sotto del limite tornerà a `0.0`, mentre tutto ciò al di sopra del limite tornerà a `1.0`. Prova a cambiare il valore della soglia alla riga 20 del seguente codice.
<div class="codeAndCanvas" data="step.frag"></div>
`smoothstep()`. Data una serie di due numeri e un valore, questa funzione interpolerà il valore tra la serie definita. I primi due parametri sono per l’inizio e la fine della transizione, mentre il terzo è per il valore d’interpolazione.<div class="codeAndCanvas" data="smoothstep.frag"></div>
Nell’esempio precedente, alla riga 12, nota che abbiamo usato smoothstep ( ) per disegnare la linea verde sulla funzione
`plot()`. Per ciascuna posizione lungo l’asse x, questa funzione crea un salto ad un particolare valore di y. Come? Collegando tra di loro due `smoothstep()`. Guarda la funzione qui di seguito, sostituiscila alla riga 20 di sopra e pensa ad esso come ad un taglio verticale. Lo sfondo assomiglia ad una linea, vero?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Seno e coseno.
Quando si vuole usare la matematica per animare, modellare o combinare, non c’è nulla di meglio che essere amici del seno e del coseno.
Queste due basiche funzioni trigonometriche lavorano congiuntamente per costruire cerchi che sono utili come il coltellino svizzero di MacGyver. È importante sapere come si comportano e in che modo possono essere combinate. In breve: dato un angolo (in radianti), essi ritorneranno alla corretta posizione di x (cosine) e di y (sine) di un punto sul margine del cerchio con raggio uguale a 1. Il fatto che queste funzioni ritornano ai valori normalizzati ( tra -1 e 1) in modo così armonioso e fluido le rende uno strumento incredibile.
Mentre è difficile descrivere tutte le relazioni tra le funzioni trigonometriche e i cerchi, le animazioni soprastanti le riassumono molto bene visivamente.
<div class="simpleFunction" data="y = sin(x);"></div>
Guarda attentamente questa curva sinusoidale. Nota come i valori di y ondeggiano dolcemente tra +1 e -1. Come abbiamo visto nell’esempio del tempo nel capitolo precedente, si può usare questo comportamento ritmico di
`sin()` per animare le proprietà. Se stai leggendo questo esempio su un browser, vedrai che puoi cambiare il codice nella formula soprastante per osservare come cambia l’onda. (Nota: non dimenticare il punto e virgola alla fine delle righe).Prova questi esercizi e osserva cosa succede:
Aggiungi tempo (
`u_time`) a x prima di calcolare `sin`. Osserva attentamente questo movimento lungo x*. Moltiplica x* per
`PI` prima di calcolare `sin`. Nota come le due fasi si restringano e ciascun ciclo si ripeta tra due valori interi. Moltiplica tempo (
`u_time`) per x* prima di calcolare `sin`. Osserva come la frequenza tra le fasi diventi sempre più compressa. Nota che u_time può avere già assunto un valore molto grande, rendendo il grafico difficile da leggere.`sin(x)`. Osserva come tutte le onde siano dislocate verso l’alto e adesso tutti i valori siano tra 0.0 e 2.0.`sin(x)` per 2.0. Osserva come l’ampiezza raddoppi di misura.`abs()`) di `sin(x)`. Sembra la traccia di una palla che rimbalza.`fract()`) della risultante di `sin(x)`.`ceil()`) e il più basso (`floor()`) della risultante di `sin(x)` per ottenere un’onda digitale dei valori 1 e -1.Altre funzioni utili
AAl termine dell’ultimo esercizio abbiamo presentato alcune nuove funzioni. È arrivato il momento di provare con ciascuna, decommentando le righe sottostanti, una alla volta. Impara quete funzioni e studia il loro comportamento. Lo so, ti stai chiedendo perché e se cerchi velocemente su Google "arte generativa" capirai subito. Ricorda che queste funzioni sono il nostro recinto. Stiamo controllando il movimento in una dimensione, su e giù. Presto arriverà il momento per due, tre e quattro dimensioni!
<div class="simpleFunction" data="y = mod(x,0.5); // restituisce il modulo di 0.5
//y = fract(x); // restituisce solo la parte frazionale di un numero
//y = ceil(x); // il numero intero più vicino che sia più grande o uguale a x
//y = floor(x); // il numero intero più vicino che sia più piccolo o uguale a x
//y = sign(x); // estrae il segno di x
//y = abs(x); // restituisce il valore assoluto di x
//y = clamp(x,0.0,1.0); // costringe x a restare fra 0.0 e 1.0
//y = min(0.0,x); // restituisce il valore più piccolo fra x e 0.0
//y = max(0.0,x); // restituisce il valore più grande fra x e 0.0"></div>
Funzioni di forma avanzate
Golan Levin possiede un’ampia documentazione molto utile sulle funzioni di forma complesse. Trasportare queste funzioni in GLSL è un ottimo modo per incominciare a costruire la propria cassetta degli attrezzi per creare codici.
* Polynomial Shaping Functions: www.flong.com/archive/texts/code/shapers_poly
* Exponential Shaping Functions: www.flong.com/archive/texts/code/shapers_exp
* Circular & Elliptical Shaping Functions: www.flong.com/archive/texts/code/shapers_circ
* Bezier and Other Parametric Shaping Functions: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Come i cuochi collezionano con passione spezie e ingredienti esotici, così gli artisti digitali e i creative coders adorano lavorare sulle proprie funzioni di forma.
Iñigo Quiles possiede una grande raccolta di funzioni molto utili. Dopo aver letto quest'articolo, leggi le seguenti traduzioni di queste funzioni in GLSL. Fai attenzione ai piccoli cambiamenti necessari, come mettere "."" (punto) sui valori flessibili, e usare il nome GLSL per le funzioni in C; per esempio, invece di
`powf()`, usa `pow()`:
<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Per mantenere alta la tua motivazione, qui trovi un esempio elegante dell’essere esperti nel karate delle funzioni di forma (creato da Danguafer).
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
Nel Prossimo >> capitolo inizieremo ad usare nuove mosse. Prima mescolando i colori e poi disegnando forme.
#### Esercizio
Guarda la seguente tabella di equazioni fatta da Kynd. Osserva come combini le funzioni e le loro proprietà per controllare i valori tra 0.0 e 1.0. Ora è il tuo momento di fare pratica replicando queste funzioni. Ricordati: più ti eserciti, migliore sarà il tuo karate.
#### Per la tua cassetta degli attrezzi
Qui trovi alcuni strumenti che ti faciliteranno la visualizzazione di questi tipi di funzione.
* GraphToy: ancora una volta Iñigo Quilez ha creato un mezzo per visualizzare le funzioni GLSL in WebGL.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README Jp (05/README-jp.md)
アルゴリズムで絵を描く
シェイピング関数
この章は「ミヤギさんの壁塗りレッスン」とでもすれば良かったかもしれません。以前の章では正規化したx座標とy座標の値をr(赤)とg(緑)のチャンネルに割り当てました。つまり私たちは既に、2次元のベクトル(x, y)を受け取って4次元のベクトル(r, g, b, a)を返す関数を作ったのです。
しかし、次元をまたいでのデータの変形にさらに深く踏み込む前に、ずっとシンプルなことから修行を始めなければなりません。まずは1次元の関数を理解します。より多くの時間と労力を割いて鍛錬すれば、その分だけあなたのシェーダーカラテは強くなるでしょう。
私たちにとってミヤギさんの壁に当たるものは、下記に示すコードの枠組みです。この枠組みを使って、正規化されたx座標の値を2種類の方法で目に見えるようにしてみます。1つめの方法では色の明るさを使い(黒から白への綺麗なグラデーションを見てください)、2つめでは緑色の線を描画します(ここではx座標の値をそのままy座標に割り当てています)。
`plot` 関数のことは今はあまり気にしないでください。すぐ後で詳しく説明します。<div class="codeAndCanvas" data="linear.frag"></div>
上記のサンプルの20行目と26行目を見てください。
`vec3` 型のコンストラクタに値を1つだけ渡すと、コンストラクタは3つの色のチャンネルに同じ値を割りてようとしているのだと解釈します。一方で `vec4` は三次元のベクトルともう1つの値(ここではalpha、つまり透明度)で初期化されています。このコードがミヤギさんの壁です。よく観察して理解しておくことが大切です。この 0.0 から 1.0 の空間には何度も立ち帰ることになるでしょう。そしてあなたはいずれ、色と形を操る技をマスターするのです。
このサンプルのx座標とy座標(または明るさ)の1対1の対応は線形補間と呼ばれています。
ここから私たちは数学的な関数を使って線を形作っていくことになります。例えばxを5乗すれば曲線を作ることができます。
(訳注:ここでは x=0.0 から x=1.0 の間のグラフが一次方程式で書ける、つまりグラフが直線になることを指して線形補完 Liniear Interpolationという言葉が使われています。参考:Wikipedia: 線形補間。この章の原文ではInterpolateという単語が複数回登場しますが、厳密な使い方ではない箇所もあるため以下では「補完」という訳語はあてませんでした。興味がある方はコンピュータグラフィックスやアニメーションの世界で補完関数 Interpolator がどのように使われているか調べてみましょう。)
<div class="codeAndCanvas" data="expo.frag"></div>
面白いでしょう? 19行目の指数(
`5.0`)を他の数に変えてみましょう。例えば値を `20.0`, `2.0`, `1.0`, `0.0`, `0.2`, `0.02` に変えてみます。この値と指数の関係を理解しておくととても役立ちます。この例のように数学的な関数を様々な場面で用いると、コードを表現豊かに操ることができます。鍼で気の流れを操るようにデータの流れを操るのです。`pow()` はその中の1つです。ほとんどのネイティブ関数はハードウェアのレベルで高速に処理されるので、適切に使えばより速いコードを書くことができます。`exp(st.x) - 1.0`, `log(st.x - 1.0)`, `sqrt(st.x)` など他の関数で置き換えてみましょう。StepとSmoothstep
GLSLには他にも、値を変化させるのに使うことができるネイティブ関数があります。これらもまたハードウェアで高速に処理されます。
`step()` 関数は2つのパラメーターを受け取ります。1つめは境界または閾値で、2つめはこの関数によってチェックされる値です。境界より小さい値には全て `0.0` を返し、境界以上の値には `1.0` を返します。下記のコードの20行目の閾値を変えて試してみてください。
<div class="codeAndCanvas" data="step.frag"></div>
`smoothstep()`と呼ばれる関数があります。2つの数値からなる範囲ともう1つの値を受け取ると、この関数はその範囲の間で0.0から1.0まで滑らかに変化する数値を返します。最初の2つのパラメータは値の変化の起こる範囲の始まりと終わりで、3つめのパラメータはチェックの対象になる値です。<div class="codeAndCanvas" data="smoothstep.frag"></div>
`smoothstep()` が `plot()` 関数の中で緑色の線を描画するために使われていますね。この関数をx軸に沿って見ていくと、それぞれの点で特定のyの値に対して急激に高い値を返しています。どうなっているのでしょう。`plot` 関数は2つの `smoothstep()` を組み合わせてこれを実現しています。下記の関数を見てください。上の20行目をこの関数で置き換えて、結果を `plot` 関数のグラフを垂直に切った断面だと考えてみましょう。背景が線のように見えるでしょう?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
(訳注:おまけ。
`plot` 関数を理解するには引き算の片側を消して `smoothstep(0.2,0.5,st.x)` もしくは `smoothstep(0.5,0.8,st.x)` だけにしてみるのも良いと思います。2つのグラフの位置が少しずれていて、引き算をすると真ん中だけが残されるのが分かりますね。20行目の `smoothstep( pct-0.02, pct, st.y) -
smoothstep( pct, pct+0.02, st.y)` でも同じことを試してみましょう。)
サインとコサイン
数学を使ってアニメーションや形を作ったり、値を組み合わせたりしたいのであれば、サインとコサインに親しんでおくに越したことはありません。
この2つの基本的な三角関数を一緒に使うと円を作ることができます。これは冒険野郎マクガイバーのスイスアーミーナイフ並みに便利です。サインとコサインがどのように振る舞い、どのような組み合わせで使えるのかを知っておくことが大事です。かいつまんで言うと、これらの関数は(ラジアンを単位とする)角度を受け取り、半径を1とする円周上の点のx座標 (cosine) とy座標(sine) を返します。滑らかに変化する正規化された値(-1から1の間の値)を返す性質のおかげでサインとコサインは非常に便利な道具になります。
三角関数と円の関係について全てを言葉で説明するのは難しいのですが、上のアニメーションはそれを見事に示してくれています。
<div class="simpleFunction" data="y = sin(x);"></div>
このサイン波をよく見てください。y座標の値が+1から-1の間をスムーズに上下しているのがわかるでしょう。時間を使った以前のサンプルでも見たとおり、このサインの周期的な動きはアニメーションに使えます。ブラウザ上で読んでいる方は、上のサンプルの数式を書き換えて波の動きを変えてみることができます(行の最後のセミコロンを忘れないようにしてください)。
下記を試して何が起きるか見てみましょう。
*
`sin` を計算する前に時間の値(`u_time`)をxに足してみましょう。x座標に沿った動きを身につけましょう。*
`sin` を計算する前に、xに `PI` を掛けてみましょう。波の幅が縮まり、xが 2 進むごとにyが1サイクル上下するのを確認しましょう。*
`sin`を計算する前に時間の値(`u_time`)をxに掛けてみましょう。周波数が次第に増えて波の幅が狭くなっていく様子を見てください。u_timeが既に大きくなりすぎていてグラフが読めないほどになってしまっているかもしれないので注意してください。(訳注:もしグラフが線ではなく黒い靄のようなノイズになってしまう場合は、一度ページをリロードしてからもう一度試してください。u_timeはページを読み込んでからの累積時間なので、ここまで読み進める間にかなり大きな値になっている可能性があります)。*
`sin(x)` に 1.0 を足してみましょう。値が 0.0 から 2.0 の間に収まるように波の位置が上にずれる様子を見てください。*
`sin(x)` に 2.0 を掛けてみましょう。振幅が倍になります。*
`sin(x)` の絶対値(`abs()`)を計算してください。弾むボールの軌跡のように見えるでしょう。*
`sin(x)` の値から少数部分だけを取り出してみましょう(`fract()`)。`sin(x)` の値を超える最小の整数(`ceil()`)と、それより小さい最大の整数(`floor()`)を足して、1 と -1 だけからなるデジタル波を作ってみましょう。便利な関数あれこれ
上の課題の中ではいくつかの新しい関数を紹介しました。今度は下記のサンプルのコメントを1つ1つ外して実験してみましょう。それぞれの関数がどのような振る舞いをするか調べて理解しておいてください。どうしてこんなことが必要なのかと思っていますね? Googleで「generative art」を検索してみれば答えがわかります。この関数たちは私たちにとってのミヤギさんの壁なのです。今は1次元、上下の動きをマスターしつつあります。すぐに2次元、3次元、そして4次元についても学んでいきます。
<div class="simpleFunction" data="y = mod(x,0.5); // return x modulo of 0.5
//y = fract(x); // return only the fraction part of a number
//y = ceil(x); // nearest integer that is greater than or equal to x
//y = floor(x); // nearest integer less than or equal to x
//y = sign(x); // extract the sign of x
//y = abs(x); // return the absolute value of x
//y = clamp(x,0.0,1.0); // constrain x to lie between 0.0 and 1.0
//y = min(0.0,x); // return the lesser of x and 0.0
//y = max(0.0,x); // return the greater of x and 0.0 "></div>
より高度なシェイピング関数
Golan Levin(Golan Levin)が書いた、より複雑で非常に便利なシェイピング関数についての素晴らしいドキュメントがあります。自分のスニペット集を作るために、これらの関数をGLSLに移植してみると良いでしょう。
* Polynomial Shaping Functions: www.flong.com/archive/texts/code/shapers_poly
* Exponential Shaping Functions: www.flong.com/archive/texts/code/shapers_exp
* Circular & Elliptical Shaping Functions: www.flong.com/archive/texts/code/shapers_circ
* Bezier and Other Parametric Shaping Functions: www.flong.com/archive/texts/code/shapers_bez
スパイスや珍しい食材を集めるシェフのように、デジタルアーティストやクリエィティブコーダーは自らシェイピング関数に取り組むのが大好きです。
Iñigo Quilesは便利な関数の素晴らしいコレクションを持っています。 この記事をまず読んでから、下記にあるGLSLへ翻訳したバージョンを見てみましょう。浮動小数点の数値に「.(小数点)」を追加したり、Cの関数をGLSL特有の関数名で置き換える、例えば
`powf()` の代わりに `pow()` を使うなどの細かな変更点に注意してください。* Impulse
* Cubic Pulse
* Exponential Step
* Parabola
* Power Curve
匠の技を見てモチベーションを上げましょう。Danguaferの作品です。
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
次の章では新しい技を使います。まず色の調合から始めて、次に形を描いていきます。
#### 演習
Kyndが作った数式の表を見てください。数値と関数をどのように組み合わせて、0.0 から 1.0 の間で値をコントロールしているかを読み取りましょう。実際に関数を置き換えて練習してください。鍛える程にあなたのカラテは強くなります。
#### 便利なツール
この種の関数を簡単に視覚化するためのツールをいくつか紹介します。
* GraphToy: 先ほども紹介したIñigo Quilezが作った、GLSLの関数をWebGLで視覚化するためのツールです。
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README Kr (05/README-kr.md)
Algorithmic drawing
Shaping functions
이번 챕터는 "미야기씨의 울타리 수업"이라고 표현할수 있겠다. 전에는, 노멀라이즈된 ,x와 y를 red와 green채널들에 적용해 보았다.
원래는 2차원 벡터(x and y)를 받아, 4차원 벡터(r, g, b and a)를 리턴하는 함수가 좀더 자연스럽니다. 하지만 지금 이런문제를 해결하기 전에, 간단한것부터 시작해보자... 훨씬더 간단한것부터. 이말은 1차원 함수에 대한 정의에서부터 시작한다. 시간과 에너지를 많이 투자하고 배우고 마스터하려고 하다보면, 우리는 더욱더 강한 쉐이더 카라테를 배울수 있을것이다.
아래 코드는 우리의 울타리가 될것이다. 여기서 우린 x 위치정보(
`st.x`)의 노멀라이즈된 값을 2가지 방법으로 시각화 하는데: 하나는 밝기로 (검은색에서 흰색으로 잘 묻어져 나가는 색), 다른 하나는 그 위에 초록색 선( x 값이 바로 y 값에 적용된)으로 나타낼 것이다. 지금은 선을 그리는 함수에 대해 너무 신경쓰지말자. 그부분은 나중에 더 자세히 다루게 될것이다.<div class="codeAndCanvas" data="linear.frag"></div>
퀵노트:
`vec3` 타입 생성자는 유저가 세가지 컬러 채널들을 입력하려는 것을 "이해" 한다. 또한, `vec4` 타입생성자역시, 3차원 벡터와, 1개의 실수(보통 alpha나 opacity라고 일컫는)로 이루어진 4차원 벡터를 만들려고 한다는 점 또한 이해한다. 라인 19과 25을 위에 코드에서 찾아보라.이 코드는 다시한번 당신의 울타리이다; 받아들이고 이해하는것이 중요하다. 앞으로도 계쏙 이 0.0 에서 1.0의 공간을 계속 사용하게 될것이다. 여기서 당신은 블렌딩과, 선그리기를 마스터 해야한다.
x와 y (또는 밝기)의 1:1 대응은 흔히 선형 보간법 이라고 알려져 있다. 여기서 우린 수학적 함수로 선을 그릴수 있다. 예로, x값이 지수로 5를 갖는다면 이 선은 구부러 진다.
<div class="codeAndCanvas" data="expo.frag"></div>
흥미로운일이지 않은가? 라인 19에서 다른 값들을 시도해보자: 20.0, 2.0, 1.0, 0.0, 0.2, 0.02. 지수를 통해 나오는 값에 대한 상관관계를 이해하는 것은 앞으로 매우 유용한 권법이 될것이다. 이런 종류의 수학적 함수를 코드의 공간에서 이용하는것은 엄청난 표현 컨트롤을 할수 있도록 허용한다. 마치 데이터를 이용한 한방권법이라고 저자는 소개한다.
`pow()` 는 GLSL의 내장형 함수이고 GLSL은 이런 수학함수를 많이 제공한다. 거의 모든 함수들은 하드웨어 레벨에서 구동되도록 '가속화' 되어 있다. 다시말해, 이런 이점을 잘 활용하면 굉장히 빠른 코드를 쓸수 있게 된다.`exp()`, `log()`, `sqrt()`. 어떤함수들은 인자로 PI값을 넣었을때 재밌는 결과를 내기도 한다. 라인 5에 `PI`값을 `3.14159265359`로 define해 둔것을 알수 있다.Step and Smoothstep
GLSL에는 하드웨어 가속화된 내장 보간법 함수들이 존재한다.
`step()` 보간법은 2개의 인자를 받는다. 첫번째것은 경계나, 한계점이고, 두번째것은 체크하거나 넘길 값이다. 어떤값이든 이 한계점보다 낮은 값은 `0`으로 처리되어 리턴될것이고, 한계점보다 높은 값은 `1.0`으로 처리되어 리턴될것이다.라인 20의 한계점 값을 바꾸어 보자.
<div class="codeAndCanvas" data="step.frag"></div>
`smoothstep()`이다. 두값의 레인지를 주고, 그 사이의 값으로 보간시켜주는 방법이다. 처음 두개의 인자는 레인지의 시작점과 끝점이고, 세번째 인자는 두 점사이에서 보간되고 리턴되는 값이다.<div class="codeAndCanvas" data="smoothstep.frag"></div>
전 예제의 라인 12를 보면, smoothstep을
`plot()`에 사용하여 초록색 선을 그린것을 알수 있다. 각 x값에 대한 y값을 bump시킨 것으로 표현되고 있는데. 실제로 `smoothstep()` 를 통해 그려진 쉐이프 A를 같은 방법으로 그려진 쉐이프 B에서 그냥 빼준것이다. 아래 예제를 보고 라인 20과 바꿔보아라. ght?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sine과 Cosine 함수
수학으로 애니메이션, 쉐이프등을 만들거나 섞을때는, sine과 cosine만큼 좋은 친구도 없다.
이 두개의 삼각함수로 원을 그리는 유명한 예제를 보면 마치 맥가이버의 스위스 군용 나이프와도 같다. 이들이 왜 이렇게 작동되고, 서로 영향을 주는지 확실히 이해를 해볼 필요가 있다. 요약하자면, 이 함수들로 전해지는 인자값(흔히 radians 값)에 따라, 반지름이 1인 동그라미 모양을 그리는 선 위에 모든 x (cosine) 와 y (sine)가 나타내어 진다고 볼수 있다. 그리고, 이 값들은 노멀라이즈된 값(-1 에서 1 사이의 값)으로 리턴되는 이 방식은 굉장히 유용한 툴로 알려져 있다.
삼각함수들과 동그라미에 대한 해석등을 전부 다를수는 없지만, 위에 애니메이션을 통해 이들이 할수 있는 일이 얼마나 간단하고 아름다운지 살펴볼수 있다.
<div class="simpleFunction" data="y = sin(x);"></div>
전 챕터의 time 예제에서 봤듯이, sine wave를 통해, y값이 얼마나 부드럽게 +1에서 -1으로 이동되는지 확인해보라.
`sin()`의 리드믹한 결과값은 애니메이션에 굉장히 유용하다. 브라우져에서 이 예제를 읽고 있다면 코드를 변경해서 변화를 시도해보자. (노트: 끝에 semicolon은 까먹지 말고 붙히자.)아래의 예제들을 실행해보고, 어떤일들이 벌어지는지 관찰해보자:
`sin`을 계산하기 전에 time(`u_time`)을 x 적용해보자. x*에 대한 움직임을 습득해보자.
`sin`을 계산하기 전에 x*에 `PI`를 곱해보자. 반복구간이 좁혀 지는것을 확인할수 있다. time (
`u_time`) 와 x값을 `sin`에 적용되기 전에 곱해보자. 반복성이 잦아지는것을 확인할수 있다. u_time이 커지면서 그래프를 점차 읽기 힘들어지게 된다는 것 또한 확인할수 있다.`sin(x)`에 1.0을 더해보자. 전체 웨이브가 이동 되는 것을 확인할수 있다. 이제 모든 값들은 0.0에서 2.0사이로 구간이 바뀌었다.`sin(x)`에 2.0을 곱해보자. 웨이브의 전체적 강도가 2배로 변하는 것을 알수 있다.`abs()`)을 `sin(x)`에 적용해보자. 공이 튕기는것 같은 효과를 볼수 있다.`fract()`)만 취하고, 이를 `sin(x)`에 적용해보자.`ceil()`)된 수나 내림(`floor()`)된 수 역시 `sin(x)`에 적용해보고, -1에서 1값이 어떻게 리턴되는지 살펴보자.키타 유용한 함수들
마지막에 여러 새로운 함수들을 소개했다. 이제 이들을 가지고 하나씩 적용을 해볼차례이다. 하나씩 uncomment를 하면 결과를 확인할수 있을것이다.이들에 대해 샅샅히 파내어 익혀보자. 혹시나 왜그래야 하는지에 대한 의문이 든다면, 좋은 대답중 하나는 "generative art"에 대한 것이다. 이 함수들이 우리의 울타리 하는 것을 생각하자. 우리는 지금 1차원에서 권법을 연마하는 중인것이다. 곧, 2차원, 3차원, 4차원까지 우리는 진입하게 될것이다!
<div class="simpleFunction" data="y = mod(x,0.5); // 0.5로 나눈 나머지
//y = fract(x); // 소수점 구간만 취하기.
//y = ceil(x); // x값보다 큰 정수, 올림.
//y = floor(x); // x값보다 작은 정수, 내림.
//y = sign(x); // x의 부호 취득.
//y = abs(x); // x의 절대값 구하기.
//y = clamp(x,0.0,1.0); // x값이 0.0과 1.0에 존재하도록 자른다.
//y = min(0.0,x); // x가 0.0보다 작으면 x, 크면 0.0
//y = max(0.0,x); // x가 0.0보다 크면 x, 작으면 0.0 "></div>
고급 쉐이핑 함수들
Golan Levin 는 제법 복잡하지만 굉장히 유용한 쉐이핑 함수들을 잘 정리해 두었다. 이들을 GLSL로 포팅하는것또한 굉장히 유용한 공부방법일 것이다. 자신만의 쉐이핑 펑션 라이브러리를 개설해보자.
* Polynomial Shaping Functions: www.flong.com/archive/texts/code/shapers_poly
* Exponential Shaping Functions: www.flong.com/archive/texts/code/shapers_exp
* Circular & Elliptical Shaping Functions: www.flong.com/archive/texts/code/shapers_circ
* Bezier and Other Parametric Shaping Functions: www.flong.com/archive/texts/code/shapers_bez
요리사가 자신만의 요리재료와 소스 노하우를 모으듯, digital artist들이나, creative coder들 역시 그들만의 쉐이핑 펑션을 만드는 버릇을 가져야 할것이다.
Iñigo Quiles가 가지고 있는 useful functions정리는 매우 유용하다. 이 글을 읽고, GLSL로 번역해보는 작업도 해볼수 있다. 조심해야 될 점들은, "."(점)을 실수 뒤에 꼭 넣어야 한다는 점이다. 함수의 이름이
`powf()` 에서 `pow()`등으로 바뀌어 구현되는 점등이다.* Impulse
* Cubic Pulse
* Exponential Step
* Parabola
* Power Curve
계속 작업이 뜨겁게 진행되는것을 도울수 있게 여기 멋진 작업도 소개해본다. (made by Danguafer)
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
다음 >> 챕터에서 우리는 새로운 동작에 대해 배우게 될것이다. 색에 조합에 대해 배우고, 쉐이프를 창조하는 작업일것이다.
#### 연습
아래의 공식 테이블을 한번 살펴보자. Kynd 수학함수들이 서로 어떻게 섞여지는지 살펴보고, 이 값들이 0.0에서 1.0사이에서 어떻게 바뀌어 가는지 보자. 이제 스스로 함수들을 복제하고 조합해볼 차례이다. 기억하자. 연습을 할수록 당신의 카라테가 늘게 될것이라는 것을.
#### 당신의 툴박스를 위해
수학함수를 쉽게 시각화 할수있는 툴을 소개해보고자 한다.
* GraphToy: Iñigo Quilez의 GLSL함수들을 WebGL에서 시각화 하는 툴이다.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README Pl (05/README-pl.md)
Rysowanie algorytmiczne
Shaping functions
Rozdział ten mógłby się nazywać "lekcja płota Pana Miyagiego". Poprzednio, mapowaliśmy znormalizowane pozycje x i y do czerwonego i zielonego kanału. W skróćie, stworzyliśmy fumkcję, która przyjmuje dwuwymiarowy wektor (x i y) i zwraca czterowymiarowy wektor (r, g, b i a). Jednak zanim zagłębimy się w takie transformacje między wymiarami, wypada najpierw opanować tworzenie jednowymiarowych funkcji w pełni. Im więcej czasu spędzisz na szlifowaniu tej umiejętności, tym lepsze będzie twoje "shader karate".
Poniższy kod będzie naszym płotem. Wizualizujemy w nim znormalizowaną wartość współrzędnej x (
st.x) w dwojaki sposób: poprzez jasność (zauważ płynny gradient od czerni do bieli) oraz przez rysowanie zielonego wykresu funkcji (w poniższym przykładzie funkcji x = y). Nie przejmuj się za bardzo funkcją plot - wrócimy do niej później. <div class="codeAndCanvas" data="linear.frag"></div>
Krótka uwaga: Konstruktor typu
vec3 "rozumie", że chcesz przypisać tę samą wartość do każdego z trzech kanałów koloru, natomaist typu vec4 rozumie, że chcesz stworzyć czterowymiarowy wektor z wektora trójwymiarowego i czwartej wartości (w tym wypadku wartość ta kontroluje alphę, czyli przezroczystość). Spójrz na linjki 19 i 25 powyżej.Kod jest twoim płotem - ważne, żebyś umiał na niego spojrzeć i go zrozumieć. Zakres od 0.0 do 1.0 będzie ci stale towarzyszył. Opanujesz sztukę blendowania (pol. "mieszania", "łączenia") i kształtowania tej linii
Tę wzajmnie jednoznaczną (ang. "one-to-one") funkcję między x i y (lub jasnością) nazywamy interpolacją liniową. Możemy użyć funkcji matematycznych by uformować tę linię. Przykładowo, możemy podnieść x do potęgi 5, aby utworzyć krzywą linię.
<div class="codeAndCanvas" data="expo.frag"></div>
Ciekawe, prawda? W linijce 22 spróbuj użyć następujących wykładników: 20.0, 2.0, 1.0, 0.0, 0.2 lub 0.02. Zrozumienie związków między końcową wartością a wykładnikiem będzie bardzo przydatne. Używanie tego typu funkcji matematycznych tu i tam da ci pełnię kontroli nad twoim kodem.
pow() to natywna funkcja w GLSL, więc zaimplementowana jest na poziomie hardware'u, co oznacza większą wydajność.Zastąp funkcję potęgową w linijce 22 inną natywną funkcją. Spróbuj
exp(), log() i sqrt(). W wypadku funkcji trygonometrycznych warto użyć liczby PI. Jest ona zdefiniowana w 8 linijce za pomocą makra, który zastąpi każde użycie PI wartością 3.14159265359.Step i Smoothstep
GLSL posiada też unikalne natywne funckje interpolacyjne wykorzystujące akcelerację sprzętową.
step() otrzymuje dwa argumenty. Pierwszy z nich to limit lub próg, natomiast drugi to wartość, którą chcemy porównać z tym progiem. Każda wartość poniżej limitu zwróci 0.0, natomiast wszystko powyżej limitu zwróci 1.0.Spróbuj zmienić tę wartość progową w linijce 20 poniższego kodu.
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep() i otrzymuje trzy argumenty Dwa pierwsze argumenty służą do określenia początku i końca interpolacji (tranzycji/przejścia), natomiast trzeci jest wartością interpolowaną.<div class="codeAndCanvas" data="smoothstep.frag"></div>
W poprzednim przykładzie, w linii 12, zauważ, że użyliśmy smoothstep wewnątrz funkcji
plot() do narysowania zielonej linii. Dla każdej pozycji wzdłuż osi x funkcja ta zwraca odpowiednią wartość y. Jak? Poprzez połączenie ze sobą dwóch smoothstep(). Spójrz na poniżśzy fragment kodu (zauważ, że jest analogiczny do kodu w funkcji plot()) i wstaw go w linijce 20 powyższego przykładu. Wyobraź sobie, że widoczny wykres o kształcie dzwona (o kształcie rozkładu normalnego) "wędruje" wzdłuż funkcji przekazanej do argumentu pct funkcji plot().glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sinus i Cosinus
Kiedy chcesz użyć matematyki do animacji, kształtowania lub blendowania wartości, nie ma nic lepszego niż zaprzyjaźnienie się z sinusem i cosinusem.
Te dwie podstawowe funkcje trygonometryczne współpracują ze sobą, aby skonstruować okręgi, które są tak poręczne jak szwajcarski scyzoryk MacGyvera. Warto wiedzieć, jak się zachowują i na jakie sposoby można je łączyć. W skrócie, otrzymawszy kąt (w radianach), zwrócą one współrzędne x (cosinus) i y (sinus) punktu na brzegu okręgu o promieniu równym 1. Jednak fakt, że zwracają one znormalizowane wartości (wartości pomiędzy -1 a 1) w tak płynny sposób, czyni z nich niesamowite narzędzie.
Choć trudno opisać wszystkie związki między funkcjami trygonometrycznymi i okręgami, powyższa animacja pięknie je wizualizuje.
<div class="simpleFunction" data="y = sin(x);"></div>
Przyjrzyj się uważnie powyższej fali sinusoidalnej. Zauważ, jak wartości y oscylują płynnie między +1 a -1. Jak widzieliśmy w przykładzie z czasem w poprzednim rozdziale, możesz wykorzystać to rytmiczne zachowanie
sin() do animowania dowolnych wartości. Jeśli czytasz ten przykład w przeglądarce, spróbuj zmienić kod w powyższym jedno-linjkowym przykładzie i zaobserwuj, jak zmienia się fala. (Uwaga: nie zapomnij o średniku na końcu linii).
Spróbuj następnujących ćwiczeń i zobacz, co się stanie
Dodaj czas (
u_time) do x wewnątrz funkcji sin. Zapamiętaj ten ruch wzdłuż x*. Pomnóż x* przez
PI wewnątrz funkcji sin. Zauważ, jak okres sinusa zmniejsza się do 2. Zwiększyliśmy w ten sposób częstotliwość. Pomnóż czas (
u_time) przez x* wewnątrz funkcji sin. Zobacz jak częstotliwość zwiększa się, a okres maleje. Możliwe że u_time może być już bardzo duży, co czyni wykres trudnym do odczytania - odśwież stronę i spróbuj ponownie.sin(x). Zobacz jak cała fala jest przesunięta w górę i wszystkie wartości są pomiędzy 0.0 a 2.0.sin(x) przez 2.0. Zobacz jak amplituda podwaja się.abs()) funkcji sin(x). Wygląda to jak ślad odbijającej się piłki.fract()) z funkcji sin(x).ceil()) oraz podłogę (floor()) z sin(x), aby otrzymać falę cyfrową o wartościach 1 i -1.
Kilka dodatkowych przydatnych funkcji
Pod koniec ostatniego ćwiczenia wprowadziliśmy kilka nowych funkcji. Teraz czas na eksperymentowanie z każdą z nich poprzez odkomentowanie poniższych linii po kolei. Poznaj te funkcje i zbadaj jak się zachowują. Wiem, zastanawiasz się... dlaczego? Szybkie wyszukiwanie w google "generative art" powie Ci to. Pamiętaj, że te funkcje to nasz płot. Opanowujemy ruch w jednym wymiarze, w górę i w dół. Już niedługo przyjdzie czas na dwa, trzy i cztery wymiary!
<div class="simpleFunction" data="y = mod(x,0.5); // x modulo 0.5
//y = fract(x); // część ułamkowa x
//y = ceil(x); // sufit z x (najmniejsza liczba całkowita większa lub równa x)
//y = floor(x); // sufit z x (największa liczba całkowita mniejsza lub równa x)
//y = sign(x); // signum z x (+ lub - w zależności od znaku x)
//y = abs(x); // wartość bezwzględna z x
//y = clamp(x,0.0,1.0); // ogranicz x do zakresu od 0.0 do 1.0
//y = min(0.0,x); // minimum z x i 0.0
//y = max(0.0,x); // maksimum z x i 0.0 "></div>
Zaawansowane funkcje kształtujące
Golan Levin ma świetną dokumentację bardziej złożonych shaping functions, które są niezwykle pomocne. Samodzielene przeniesienie ich do GLSL jest dobrym krokiem w stronę budowania własnego zasobu snippetów kodu.
* Wielomianowe shaping fucntions: www.flong.com/archive/texts/code/shapers_poly
* Wykładnicze shaping functions: www.flong.com/archive/texts/code/shapers_exp
* Kołowe i Eliptyczne shaping functions: www.flong.com/archive/texts/code/shapers_circ
* Parametryczne (Beziera) shaping functions: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Podobnie jak szefowie kuchni, którzy zbierają przyprawy i egzotyczne składniki, artyści cyfrowi mają szczególne zamiłowanie do pracy nad własnymi shaping functions.
Iñigo Quiles ma wspaniałą kolekcję użytecznych funkcji. Po przeczytaniu tego artykułu spójrz na poniższe tłumaczenie tych funkcji na GLSL. Zwróć uwagę na małe zmiany, jak stawianie "." (kropki) przy liczbach zmiennoprzecinkowych i używanie nazw GLSL dla funkcji z C (na przykład zamiast
powf() użyj pow()): <div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Aby podtrzymać twoją motywację, oto elegancki przykład (wykonany przez Danguafer) ilustrujący opanowanie karate shaping functions.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
W następnym rozdziale zaczniemy używać naszych nowych sztuczek. Najpierw z mieszaniem kolorów, a potem z rysowaniem kształtów.
#### Ćwiczenia
Przyjrzyj się poniższej tabeli wzorów wykonanej przez Kynd. Zobacz jak łączy on funkcje i ich właściwości, aby kontrolować wartości pomiędzy 0.0 a 1.0. Teraz nadszedł czas, abyś poćwiczył, odtwarzając te funkcje. Pamiętaj im więcej będziesz ćwiczył tym lepsze będzie twoje karate.
#### Pomocne narzędzia
Oto kilka narzędzi, które ułatwią ci wizualizację tego typu funkcji.
* Grapher: jeśli masz komputer z systemem MacOS, wpisz
grapher w swoim Spotlight, a będziesz mógł użyć tego super poręcznego narzędzia.* GraphToy: po raz kolejny Iñigo Quilez stworzył narzędzie do wizualizacji funkcji GLSL w WebGL.
* Shadershop: to niesamowite narzędzie stworzone przez Toby Schachman nauczy cię jak konstruować złożone funkcje w niesamowicie wizualny i intuicyjny sposób.
---
05/README Pt (05/README-pt.md)
Algoritmos de desenho
Modelagem de funções
Este capítulo poderia se chamar "Lição de pintar a cerca com o Sr. Miyagi". Anteriormente, nós mapeamos as posições normalizadas de x e y para os canais red e green. Essencialmente, nós criamos uma função que recebe um vetor bidimensional (x e y) e retornamos um com quatro dimensões (vermelho, verde, azul e alpha). Mas antes de irmos além de transformações dos dados entre as dimensões, precisamos fazer algo mais simples... muito mais simples: entender como criar uma função unidimensional. Quanto mais energia e tempo que você gastar dominando isso, mais forte seu karatê de shader será.
A estrutura do código a seguir será nossa cerca. Nela, nós visualizaremos os valores normalizados da coordenada x (
st.x) de dois jeitos: uma com a claridade (observe o belo gradiente de branco para preto) e a outra é traçando uma linha verde por cima (neste caso, o valor de x será atribuído diretamente a y). Não foque tanto na função de traçar, nós a veremos com mais detalhes em alguns minutos.<div class="codeAndCanvas" data="linear.frag"></div>
Nota rápida: O construtor do tipo
vec3 "entende" que você quer atribuir o mesmo valor aos três canais de cores, enquanto vec4 entende que você quer construir um vetor quadridimensional usando um vetor tridimensional e também um quarto valor (neste caso, o valor que controlará o alpha ou opacidade). Veja as linhas 19 e 25 acima por exemplo.Este código é a sua cerca: É importante observar e entendê-lo. Você voltará algumas vezes para este espaço entre 0.0 e 1.0. Aprenderá a arte de mesclar e dar forma a esta linha.
Essa relação de um pra um entre x e y (ou de brilho) é conhecida como interpolação linear. A partir daqui, nós usaremos algumas funções matemáticas para dar forma a linha. Por exemplo, nós podemos elevar x à quinta potência para criar uma linha curvada.
<div class="codeAndCanvas" data="expo.frag"></div>
Interessante, não é? Na linha 22, tente diferentes expoentes: 20.0. 2.0, 1.0, 0.0, 0.2 e 0.02 por exemplo. Entender essa relação entre o valor e o expoente será de grande ajuda. Usar esses tipos de funções matemáticas aqui e ali nos dará um controle significativo do nosso código, uma espécie de acumpultura de dados que permite que você controle a fluência dos valores.
pow() é uma função nativa no GLSL e existem muitas outras. A maioria delas são aceleradas em nível de hardware, o que significa que se elas forem usadas da forma certa e com juízo, farão seu código mais rápido.Substitua a função de potência na linha 22. Tente outras como:
exp(), log() e sqrt(). Algumas destas funções são mais interessantes quando você experimenta utilizando PI. Você pode ver na linha 8 que eu defini uma macro que retornará qualquer chamada de PI com o valor de 3.14159265359.Step e smoothstep
GLSL também tem algumas exclusivas funções nativas de interpolação que são aceleradas pelo hardware.
step() recebe dois parâmetros. O primeiro é para o limite ou limiar, enquanto o segundo é o valor que nós queremos passar. Qualquer valor abaixo do limite retornará 0.0 e qualquer um acima do limite retornará 1.0.Experimente mudar este valor de limiar na linha 20 do código a seguir.
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep(). Dado um intervalo de dois números, está função irá interpolar os valores entre o intervalo definido. Os dois primeiros parâmetros são para o início e o final da transição, enquanto o terceiro é o valor que será interpolado.<div class="codeAndCanvas" data="smoothstep.frag"></div>
No exemplo anterior, na linha 12, veja que estamos usando smoothstep para desenhar a linha verde na função
plot(). Para cada posição dentro do eixo x, esta função faz uma marcação num valor específico de y. Como? Conectando duas smoothstep() juntas. Dê uma olhada na seguinte função, troque a linha 20 acima por esta, e imagine-a como um corte vertical. O fundo se parece com uma linha, certo?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Seno e Cosseno
Quando queremos usar um pouco de matemática para animar, dar forma e mesclar valores, não há nada melhor que ser amigo do seno e cosseno.
Essas duas funções trigonométricas básicas trabalham juntas para construir circunferências que são tão úteis como o canivete suíço de MacGyver. É importante entender como elas se comportam e de quais formas elas podem ser combinadas. Em poucas palavras, dado um ângulo (em radianos), elas retornarão a posição correta de x (cosseno) e y (seno) de um ponto na linha de uma circunferência de raio igual a 1. O fato de elas retornarem valores normalizados (que vão de 1 a -1) de maneira suave faz delas, ferramentas incríveis.
Enquanto é difícil descrever todas as relações entre as funções trigonométricas e circunferências, a animação acima faz um ótimo trabalho em exemplificar visualmente esta relação.
<div class="simpleFunction" data="y = sin(x);"></div>
Preste muita atenção nesta onda de seno. Note como o valor de y flui suavemente entre +1 e -1. Como vimos nos exemplos de tempo no capítulo anterior, podemos usar esse movimento rítmico do
sin() para animar propriedades. Se você estiver lendo este exemplo em um navegador, você verá que você pode alterar o código da fórmula acima para observar como as ondas mudam. (Nota: não se esqueça do ponto e vírgula no final das linhas).Experimente com os seguintes exercícios e veja o que acontece:
Some o tempo (
u_time) ao x antes de calcular o sin. Perceba o movimento ao longo de x* Multiplique x* por
PI antes de calcular o sin. Veja como a frequência entre as fases se torna mais comprimida. Multiplique x* pelo tempo (
u_time) antes de calcular o sin. Note que u_time já pode ter se tornado um valor muito grande, o que torna difícil enxergar a linha verde.sin(x). Veja como toda a onda foi deslocada para cima e agora seus valores vão de 0.0 a 2.0.sin(x) por 2.0. Veja como a amplitude dobra seu tamanho.abs()) do sin(x). Se parece com o rastro de uma bola quicando*.fract()) do resultante do sin(x).ceil()) e o inteiro mais baixo (floor()) do resultante do sin(x) para conseguir uma onda digital de 1 e -1.Outras funções úteis
No final do último exercício nós apresentamos algumas novas funções. Agora é hora de experimentar cada uma descomentando as linhas abaixo, uma de cada vez. É importante entender o funcionamento e comportamento destas funções. Eu sei, você deve estar se perguntando... Por quê? Uma rápida pesquisa no google de "arte generativa" (ou generative art) te mostrará. Lembre-se que estas funções são a nossa cerca. Nós estamos dominando o movimento em uma dimensão, para cima e para baixo. Logo, usaremos duas, três quatro dimensões!
<div class="simpleFunction" data="y = mod(x,0.5); // return x modulo of 0.5
//y = fract(x); // return only the fraction part of a number
//y = ceil(x); // nearest integer that is greater than or equal to x
//y = floor(x); // nearest integer less than or equal to x
//y = sign(x); // extract the sign of x
//y = abs(x); // return the absolute value of x
//y = clamp(x,0.0,1.0); // constrain x to lie between 0.0 and 1.0
//y = min(0.0,x); // return the lesser of x and 0.0
//y = max(0.0,x); // return the greater of x and 0.0 "></div>
Modelando funções avançadas
Golan Levin tem uma ótima documentação de modelagem de funções complexas que são de extraordinária ajuda. Portá-las para GLSL é uma excelente forma para começar seu próprio banco de trechos de códigos.
* Polynomial Shaping Functions: www.flong.com/archive/texts/code/shapers_poly
* Exponential Shaping Functions: www.flong.com/archive/texts/code/shapers_exp
* Circular & Elliptical Shaping Functions: www.flong.com/archive/texts/code/shapers_circ
* Bezier and Other Parametric Shaping Functions: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Como chefs que coletam temperos e ingredientes exóticos, artistas digitais e programadores criativos tem um amor único por trabalhar nas suas próprias funções.
Iñigo Quiles tem uma grande coleção de funções úteis. Depois de ler este artigo, dê uma olhada na seguinte na seguinte tradução destas funções para GLSL. Preste atenção nas pequenas mudanças necessárias, como colocar o "." (ponto) nos números de ponto flutuantes ou usar o nome em GLSL para as Funções em C; por exemplo, em vez de
powf(), usamos pow():<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Para mantermos sua motivação lá em cima, aqui temos um exemplo elegante (feito por Danguafer) em masterizar o karatê das funções.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
No Próximo >> capítulo, nós começaremos a usar nossos novos movimentos. Primeiro misturando cores e em seguida, desenhando formas.
#### Exercício
Dê uma olhada na seguinte tabela de equações feita por Kynd. Veja como ele está combinando as funções e suas propriedades para controlar os valores entre 0.0 e 1.0. Agora é a hora de você praticar replicando estas funções. Lembre-se que quanto mais você praticar, melhor será o seu karatê de shaders.
#### Para sua caixa de ferramentas
Aqui temos algumas ferramentas que facilitarão a visualização destes tipos de funções.
* GraphToy: mais uma vez Iñigo Quilez fez uma ferramenta para visualizar funções em GLSL em WebGL.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/README Tr (05/README-tr.md)
Algoritmik çizim
Şekillendirme fonksiyonları
Bu bölüm "Bay Miyagi'nin çit boyama dersi" olarak adlandırılabilirdi. Daha önce, x ve y'nin normalize edilmiş konumunu kırmızı ve yeşil kanallara eşledik. Esasen iki boyutlu bir vektörü (x ve y) alıp dört boyutlu bir vektör (r, g, b ve a) döndüren bir fonksiyon yaptık. Ancak boyutlar arasında veri dönüştürmeye daha ileri gitmeden önce, daha basit başlamamız gerekiyor... çok daha basit. Bu, tek boyutlu fonksiyonlar yapmayı anlamak demektir. Bunu öğrenmeye ve ustalaşmaya ne kadar çok enerji ve zaman harcarsanız, shader karateniz o kadar güçlü olacaktır.
Aşağıdaki kod yapısı bizim çitimiz olacak. İçinde, x koordinatının (
st.x) normalize edilmiş değerini iki şekilde görselleştiriyoruz: biri parlaklıkla (siyahtan beyaza güzel gradyanı gözlemleyin) ve diğeri üzerine yeşil bir çizgi çizerek (bu durumda x değeri doğrudan y'ye atanır). Plot fonksiyonuna fazla odaklanmayın, birazdan daha ayrıntılı inceleyeceğiz.<div class="codeAndCanvas" data="linear.frag"></div>
Hızlı Not:
vec3 tip yapıcısı, üç renk kanalına aynı değeri atamak istediğinizi "anlar"; vec4 ise üç boyutlu bir vektörle artı dördüncü bir değerle (bu durumda alfa veya opaklığı kontrol eden değer) dört boyutlu bir vektör oluşturmak istediğinizi anlar. Örneğin yukarıdaki 19. ve 25. satırlara bakın.Bu kod sizin çitinizdir; onu gözlemlemek ve anlamak önemlidir. 0.0 ile 1.0 arasındaki bu alana tekrar tekrar geri döneceksiniz. Bu çizgiyi harmanlama ve şekillendirme sanatında ustalaşacaksınız.
x ile y (veya parlaklık) arasındaki bu bire bir ilişki doğrusal enterpolasyon olarak bilinir. Buradan, çizgiyi şekillendirmek için bazı matematiksel fonksiyonları kullanabiliriz. Örneğin, eğri bir çizgi yapmak için x'i 5'in kuvvetine yükseltebiliriz.
<div class="codeAndCanvas" data="expo.frag"></div>
İlginç, değil mi? 22. satırda farklı üsler deneyin: örneğin 20.0, 2.0, 1.0, 0.0, 0.2 ve 0.02. Değer ile üs arasındaki bu ilişkiyi anlamak çok faydalı olacaktır. Bu tür matematiksel fonksiyonları şurada burada kullanmak, kodunuz üzerinde ifade gücü kontrolü sağlayacaktır — değerlerin akışını kontrol etmenizi sağlayan bir tür veri akupunkturu.
pow() GLSL'de yerleşik bir fonksiyondur ve başka birçoğu daha vardır. Çoğu donanım düzeyinde hızlandırılmıştır, bu da doğru şekilde ve ölçülü kullanıldıklarında kodunuzu daha hızlı hale getirecekleri anlamına gelir.22. satırdaki üs fonksiyonunu değiştirin. Başkalarını deneyin:
exp(), log() ve sqrt(). Bu fonksiyonlardan bazıları PI ile oynadığınızda daha ilginçtir. 8. satırda, PI'nin herhangi bir kullanımını 3.14159265359 değeriyle değiştirecek bir makro tanımladığımı görebilirsiniz.Step ve Smoothstep
GLSL'de donanım hızlandırmalı bazı benzersiz yerleşik enterpolasyon fonksiyonları da vardır.
step() enterpolasyonu iki parametre alır. Birincisi sınır veya eşik değeridir, ikincisi ise kontrol etmek veya geçirmek istediğimiz değerdir. Sınırın altındaki herhangi bir değer 0.0 döndürürken, sınırın üstündeki her şey 1.0 döndürür.Aşağıdaki kodun 20. satırındaki bu eşik değerini değiştirmeyi deneyin.
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep() olarak bilinir. İki sayı aralığı ve bir değer verildiğinde, bu fonksiyon değeri tanımlanmış aralık arasında enterpolasyon yapar. İlk iki parametre geçişin başlangıcı ve sonu içindir, üçüncüsü ise enterpolasyon yapılacak değer içindir.<div class="codeAndCanvas" data="smoothstep.frag"></div>
Önceki örnekte, 12. satırda,
plot() fonksiyonunda yeşil çizgiyi çizmek için smoothstep kullandığımıza dikkat edin. x ekseni boyunca her konum için bu fonksiyon belirli bir y değerinde bir tümsek oluşturur. Nasıl mı? İki smoothstep()'i birbirine bağlayarak. Aşağıdaki fonksiyona bir bakın, yukarıdaki 20. satırın yerine koyun ve bunu dikey bir kesim olarak düşünün. Arka plan bir çizgiye benziyor, değil mi?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sinüs ve Kosinüs
Değerleri canlandırmak, şekillendirmek veya harmanlamak için biraz matematik kullanmak istediğinizde, sinüs ve kosinüsle arkadaş olmaktan daha iyi bir şey yoktur.
Bu iki temel trigonometrik fonksiyon, MacGyver'ın İsviçre çakısı kadar kullanışlı daireler oluşturmak için birlikte çalışır. Nasıl davrandıklarını ve hangi şekillerde birleştirilebileceklerini bilmek önemlidir. Özetle, bir açı (radyan cinsinden) verildiğinde, yarıçapı 1'e eşit olan bir dairenin kenarındaki bir noktanın x (kosinüs) ve y (sinüs) doğru konumunu döndürürler. Ancak normalize edilmiş değerleri (-1 ile 1 arasında değerler) bu kadar düzgün bir şekilde döndürmeleri, onları inanılmaz bir araç haline getirir.
Trigonometrik fonksiyonlar ve daireler arasındaki tüm ilişkileri tanımlamak zor olsa da, yukarıdaki animasyon bu ilişkileri görsel olarak özetleme konusunda harika bir iş çıkarıyor.
<div class="simpleFunction" data="y = sin(x);"></div>
Bu sinüs dalgasına dikkatle bakın. y değerlerinin +1 ile -1 arasında nasıl düzgün bir şekilde aktığını not edin. Önceki bölümdeki zaman örneğinde gördüğümüz gibi,
sin()'in bu ritmik davranışını özellikleri canlandırmak için kullanabilirsiniz. Bu örneği bir tarayıcıda okuyorsanız, dalganın nasıl değiştiğini izlemek için yukarıdaki formüldeki kodu değiştirebileceğinizi göreceksiniz. (Not: satırların sonundaki noktalı virgülü unutmayın.)Aşağıdaki alıştırmaları deneyin ve ne olduğunu gözlemleyin:
sin'i hesaplamadan önce x'e zaman (u_time) ekleyin. x* boyunca bu hareketi içselleştirin.
sin'i hesaplamadan önce x*'i PI ile çarpın. İki fazın nasıl küçüldüğünü ve her döngünün her 2 tam sayıda bir tekrarladığını not edin.
sin'i hesaplamadan önce zamanı (u_time) x* ile çarpın. Fazlar arasındaki frekansın nasıl giderek daha sıkıştığını görün. u_time'ın zaten çok büyük olabileceğini ve grafiğin okunmasını zorlaştırabileceğini unutmayın.sin(x)'e 1.0 ekleyin. Tüm dalganın nasıl yukarı kaydığını ve şimdi tüm değerlerin 0.0 ile 2.0 arasında olduğunu görün.sin(x)'i 2.0 ile çarpın. Genliğin nasıl iki katına çıktığını görün.*
sin(x)'in mutlak değerini (abs()) hesaplayın. Zıplayan bir topun izine benziyor.sin(x) sonucunun sadece kesir kısmını (fract()) çıkarın.sin(x) sonucunun üst tam sayısını (ceil()) ve alt tam sayısını (floor()) ekleyerek 1 ve -1 değerlerinden oluşan dijital bir dalga elde edin.Bazı ekstra faydalı fonksiyonlar
Son alıştırmanın sonunda bazı yeni fonksiyonlar tanıttık. Şimdi aşağıdaki satırları teker teker açarak her biriyle deney yapma zamanı. Bu fonksiyonları tanıyın ve nasıl davrandıklarını inceleyin. Biliyorum, merak ediyorsunuz... neden? "Generative art" (üretken sanat) için hızlı bir Google araması size söyleyecektir. Bu fonksiyonların bizim çitimiz olduğunu unutmayın. Tek boyutta harekete, yukarı ve aşağıya hakim oluyoruz. Yakında, iki, üç ve dört boyut zamanı gelecek!
<div class="simpleFunction" data="y = mod(x,0.5); // x'in 0.5'e göre modunu döndür
//y = fract(x); // sadece sayının kesir kısmını döndür
//y = ceil(x); // x'e eşit veya büyük en yakın tam sayı
//y = floor(x); // x'e eşit veya küçük en yakın tam sayı
//y = sign(x); // x'in işaretini çıkar
//y = abs(x); // x'in mutlak değerini döndür
//y = clamp(x,0.0,1.0); // x'i 0.0 ile 1.0 arasında sınırla
//y = min(0.0,x); // x ve 0.0'ın küçüğünü döndür
//y = max(0.0,x); // x ve 0.0'ın büyüğünü döndür "></div>
Gelişmiş şekillendirme fonksiyonları
Golan Levin'in olağanüstü derecede faydalı olan daha karmaşık şekillendirme fonksiyonları hakkında harika belgeleri var. Bunları GLSL'ye taşımak, kendi kod parçacığı kaynağınızı oluşturmaya başlamak için gerçekten akıllıca bir hamle.
* Polinom Şekillendirme Fonksiyonları: www.flong.com/archive/texts/code/shapers_poly
* Üstel Şekillendirme Fonksiyonları: www.flong.com/archive/texts/code/shapers_exp
* Dairesel & Eliptik Şekillendirme Fonksiyonları: www.flong.com/archive/texts/code/shapers_circ
* Bezier ve Diğer Parametrik Şekillendirme Fonksiyonları: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Baharat ve egzotik malzeme toplayan aşçılar gibi, dijital sanatçılar ve yaratıcı kodlayıcılar da kendi şekillendirme fonksiyonları üzerinde çalışmaya özel bir sevgi beslerler.
Iñigo Quiles'in harika bir kullanışlı fonksiyonlar koleksiyonu var. Bu makaleyi okuduktan sonra, bu fonksiyonların GLSL'ye aşağıdaki çevirisine bir göz atın. Gereken küçük değişikliklere dikkat edin, kayan noktalı sayılara "." (nokta) koymak ve C fonksiyonları için GLSL adını kullanmak gibi; örneğin
powf() yerine pow() kullanın:<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Motivasyonunuzu yüksek tutmak için, şekillendirme fonksiyonları karatesinde ustalaşmanın zarif bir örneği (Danguafer tarafından yapılmış):
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
Sonraki >> bölümde yeni hareketlerimizi kullanmaya başlayacağız. Önce renkleri karıştırarak, sonra şekiller çizerek.
#### Alıştırma
Kynd tarafından yapılan aşağıdaki denklem tablosuna bir göz atın. Fonksiyonları ve özelliklerini birleştirerek 0.0 ile 1.0 arasındaki değerleri nasıl kontrol ettiğini görün. Şimdi bu fonksiyonları kopyalayarak pratik yapma zamanı. Unutmayın, ne kadar çok pratik yaparsanız karateniz o kadar iyi olacak.
#### Araç kutunuz için
* LYGIA, projelerinize kolayca dahil edilebilen yeniden kullanılabilir fonksiyonlardan oluşan bir shader kütüphanesidir. Çok ayrıntılıdır, yeniden kullanılabilirlik, performans ve esneklik için tasarlanmıştır. Ve herhangi bir projeye ve framework'e kolayca eklenebilir. Farklı bölümlere ayrılmıştır ve matematik işlemleri için tam bir bölümü vardır.
* GraphToy: yine Iñigo Quilez WebGL'de GLSL fonksiyonlarını görselleştirmek için bir araç yaptı.
---
05/README Ua (05/README-ua.md)
Алгоритмічне малювання
Формотворчі функції або функції формування
Цей розділ можна було б назвати "Урок з парканом від містера Міягі". Раніше ми зіставляли нормалізоване положення координат x і y з червоним і зеленим каналами. По суті, ми створили функцію, яка приймає двовимірний вектор (x і y) та повертає чотиривимірний вектор (r, g, b і a). Але перш ніж перейти до подальшого перетворення даних між вимірами, нам потрібно почати з простіших речей... набагато простіших. З розуміння того, як користуватись одновимірними функціями. Чим більше часу та енергії ви витратите на освоєння цього і відповідну практику, тим сильнішим буде ваше шейдерне карате.
Наступний приклад коду буде нашим парканом. У ньому ми візуалізуємо нормалізоване значення координати x (
st.x) двома способами: один за допомогою яскравості (подивіться на гарний градієнт від чорного до білого), а інший шляхом побудови зеленої діагональної лінії зверху (у цьому випадку значення x присвоюється безпосередньо до y). Поки що не зосереджуйтеся занадто на функції plot. Згодом ми розберемося з нею більш детально.<div class="codeAndCanvas" data="linear.frag"></div>
Коротка примітка: конструктор типу
vec3 "розуміє", що ви хочете призначити одне і те саме значення трьом каналам, тоді як vec4 розуміє, що ви хочете побудувати чотиривимірний вектор за допомогою одного тривимірного і додаткового четвертого значення. У цьому випадку четверте значення відповідатиме за альфа-канал або непрозорість. Перегляньте ці приклади на рядках 19 і 25.Цей код — ваш паркан. Важливо бачити його і розуміти. Ви знову і знову повертатиметеся до цього простору між 0.0 і 1.0. Ви опануєте мистецтво змішування та формування подібних ліній.
Цей однозначний зв'язок між x і y (або яскравістю) відомий як лінійна інтерполяція. З цього моменту ми можемо використовувати деякі математичні функції для надання лінії певної форми, для її формування. Наприклад, ми можемо піднести x до 5 степеня, щоб отримати криву лінію.
<div class="codeAndCanvas" data="expo.frag"></div>
Цікаво, правда? У рядку 22 спробуйте різні показники для степеня: наприклад, 20.0, 2.0, 1.0, 0.0, 0.2 і 0.02. Розуміння цього зв'язку між значенням і експонентою буде дуже корисним. Використання подібних типів математичних функцій дасть вам виразний засіб для контролю вашого коду, свого роду акупунктуру даних, яка дозволить вам контролювати потік значень.
pow() — одна з багатьох вбудованих функцій GLSL. Більшість із них прискорені на апаратному рівні, а це означає, що якщо вони використовуються належним чином і з обережністю, то ваш код стане швидшим.Замініть функцію
pow у рядку 22 на якусь іншу, наприклад: exp(), log() і sqrt(). Деякі з цих функцій стають цікавіші із використанням числа PI. У рядку 8 ви можете побачити, що я визначив макрос, який замінить будь-яке використання PI на значення 3.14159265359.step і smoothstep
GLSL має деякі унікальні функції інтерполяції, які також апаратно прискорені.
step() приймає два параметри. Перший — це межа або поріг, а другий — це значення для якого ми хочемо застосувати функцію. Будь-яке значення нижче порогу поверне 0.0, а все, що його перевищує — 1.0.Спробуйте змінити у наступному коді порогове значення, що на рядку 20:
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep(). Функція плавно інтерполює значення у вказаному діапазоні з двох чисел. Перші два параметри призначені для початку та кінця перехідного діапазону, а третій — для значення, яке потрібно інтерполювати.<div class="codeAndCanvas" data="smoothstep.frag"></div>
У функції
plot() на рядку 12 попереднього прикладу, ми використали smoothstep, щоб намалювати зелену лінію. Для кожної позиції вздовж вісі x ця функція робить виступи у певному значенні y. Яким чином? Об'єднавши результат двох функцій smoothstep() разом. Погляньте на наступну функцію, замініть нею рядок 20 вище і подумайте про неї як про вертикальний розріз. Фон схожий на лінію, чи не так?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Синус і Косинус
Коли ви хочете застосувати певну математику для анімації, або при формуванні чи змішуванні значень, немає нічого кращого, ніж товаришувати з синусом і косинусом.
Ці дві основні тригонометричні функції працюють у парі при побудові кола, що є настільки ж корисним, як і швейцарський армійський ніж. Важливо знати, як вони поводяться і як їх можна комбінувати. Коротко кажучи, задаючи їм кут у радіанах, вони повертають правильне положення координати x (cos) і y (sin) точки на краю кола з радіусом, рівним 1. А той факт, що вони повертають нормалізовані значення (від -1 до 1), які до того ж достатньо плавні, робить їх неймовірним інструментом.
Хоча важко описати всі взаємозв'язки між тригонометричними функціями та колами, наведена вище анімація чудово їх узагальнює.
<div class="simpleFunction" data="y = sin(x);"></div>
Уважно подивіться на зображену вище синусоїду. Зверніть увагу, як значення y плавно змінюються між +1 і -1. Як ми бачили у прикладі зі змінною для часу, що був у попередньому розділі, ви можете використовувати цю ритмічну поведінку
sin() для анімації властивостей. Якщо ви читаєте цей приклад у браузері, то можете змінити код у формулі вище, щоб побачити, як змінюється хвиля. (Примітка: не забудьте про крапку з комою в кінці рядка.)Спробуйте виконати наступні вправи та зверніть увагу на те, що відбувається:
Додайте час (
u_time) до x перед обчисленням sin. Оцініть рух уздовж осі x*. Помножте x* на
PI перед обчисленням sin. Зверніть увагу, як дві фази скорочуються, так що кожен цикл повторюється через кожні 2 цілих числа. Помножте час (
u_time) на x* перед обчисленням sin. Подивіться, як частота між фазами стає все більш і більш стиснутою. Зверніть увагу, що u_time до цього моменту може стати вже дуже великим, що ускладнить сприйняття отриманого графіка.sin(x). Подивіться, як уся хвиля змістилася вгору, і тепер усі значення знаходяться між 0.0 і 2.0.sin(x) на 2.0. Подивіться, як амплітуда збільшилася вдвічі.abs()) sin(x). Графік стане схожим на траєкторію м'яча, що стрибає.sin(x) за допомогою fract().sin(x), округлені в більшу (ceil()) та меншу сторони (floor()), щоб отримати цифрову хвилю зі значеннями 1 і -1.Деякі додаткові корисні функції
Наприкінці останньої вправи ми представили кілька нових функцій. Настав час поекспериментувати з ними. Спробуйте по черзі розкоментувати та випробувати кожну з них. Ознайомтеся з цими функціями та вивчіть, як вони поводяться. Я знаю, вам цікаво... навіщо? Швидкий пошук у Google на тему "генеративне мистецтво" підкаже вам. Майте на увазі, що ці функції є нашим парканом. Ми опановуємо рух в одному вимірі, вгору і вниз. Вже зовсім скоро настане час для двох, трьох і чотирьох вимірів!
<div class="simpleFunction" data="y = mod(x,0.5); // return x modulo of 0.5
// y = fract(x); // повертає лише дробову частину числа
// y = ceil(x); // найближче ціле число, яке більше або дорівнює x
// y = floor(x); // найближче ціле число, яке менше або дорівнює x
// y = sign(x); // отримати знак числа x
// y = abs(x); // повертає абсолютне значення x
// y = clamp(x,0.0,1.0); // обмеження значення x в діапазоні між 0.0 і 1.0
// y = min(0.0,x); // повертає менше значення із x і 0.0
// y = max(0.0,x); // повертає більше значення із x і 0.0 "></div>
Просунуті формотворчі функції
Golan Levin має чудову документацію про складніші формотворчі функції, які є надзвичайно корисними. Перенесення їх на GLSL — це розумний крок, щоб почати створювати власний ресурс з фрагментами коду.
* Поліноміальні формотворчі функції: www.flong.com/archive/texts/code/shapers_poly
* Експоненціальні формотворчі функції: www.flong.com/archive/texts/code/shapers_exp
* Кругові та еліптичні формотворчі функції: www.flong.com/archive/texts/code/shapers_circ
* Безьє та інші параметричні функції: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Подібно до кухарів, які збирають спеції та екзотичні інгредієнти, цифрові художники та креативні кодери приділяють особливу увагу створенню власних формотворчих функцій.
Iñigo Quiles має чудову колекцію корисних функцій. Прочитавши цю статтю, перегляньте на наступні реалізації цих функцій на GLSL. Зверніть увагу на невеликі зміни, які потрібно було внести. Як-от на "." (крапку) для чисел з рухомою крапкою та заміни деяких C-функцій на GLSL аналоги: наприклад, замість
powf() використовується pow().<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Для підтримки вашої мотивації, ось елегантний приклад опанування карате формотворчих функцій. Автор Danguafer:
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
У наступному розділі ми почнемо використовувати нові карате-рухи. Спочатку почнемо зі змішування кольорів, а потім перейдемо до малювання фігур.
#### Вправа
Подивіться на наведену нижче таблицю рівнянь, створену за авторства Kynd. Подивіться, як він поєднує функції та їхні властивості, щоб контролювати значення між 0.0 та 1.0. Настав час потренуватися, самостійно відтворюючи ці функції. Пам'ятайте, що чим більше ви тренуєтеся, тим краще буде ваше карате.
#### До вашого інструментарію
Ось кілька інструментів, які полегшать візуалізацію цих типів функцій:
* GraphToy: вже згаданий Iñigo Quilez створив інструмент для візуалізації GLSL-функцій у WebGL.
* Бібліотека шейдерів LYGIA — бібліотека шейдерів із функцій, які можна легко включити та використати у ваших проєктах. Бібліотека дуже атомарна, створена для зручного перевикористання, продуктивності й гнучкості. Може бути легко додана до будь-яких проєктів та фреймворків.
---
05/README Vi (05/README-vi.md)
Các thuật toán hình học
Các hàm số cơ bản (Hàm hình dạng - Shape function)
Chương này có thể đặt tên là "bài học sơn hàng rào của ngài Miyagi". Trước đó, ta đã ánh xạ toạ độ x và y sang các kênh RED và GREEN. Ta cũng đã tạo ra một hàm nhận một vector 2 chiều (x, y) làm tham số đầu vào và trả ra một vector 4 chiều (r, g, b, a). Nhưng trước khi đi sâu vào việc biến đổi dữ liệu giữa các kiểu thì ta cần bắt đầu từ những thứ đơn giản hơn... rất nhiều. Đó là việc tạo ra các hàm chỉ xử lý vector 1 chiều. Càng bỏ thời gian và công sức học cho thuần thục kỹ năng này, võ karate-shader của bạn sẽ càng cao siêu.
Cấu trúc code dưới đây sẽ là hàng rào của chúng ta. Trong đó, ta chuẩn hoá giá trị x (
st.x) bằng 2 cách: một là với cường độ sáng (quan sát gradient từ đen sang trắng) và hai là vẽ đồ thị một đường màu xanh lá đè lên trên (trường hợp này giá trị của x được gán trực tiếp cho y). Đừng tập trung quá nhiều vào hàm vẽ đồ thị, ta sẽ đi vào chi tiết sau.<div class="codeAndCanvas" data="linear.frag"></div>
Chú thích: Hàm khởi tạo
vec3 sẽ tự động "nhận ra" bạn muốn gán màu của cả 3 kênh RGB giống nhau ở dòng 19. Còn hàm khởi tạo vec4 thì "nhận ra" bạn muốn tạo một vector 4 chiều từ một vector 3 chiều kết hợp một số thực nữa dành cho chiều thứ tư, ở dòng 25.Đoạn code này là hàng rào của bạn; nên hãy tập trung quan sát kỹ. Bạn sẽ còn sử dụng khoảng
0.0 và 1.0 này nhiều lần nữa. Bạn sẽ thuần thục kỹ năng tạo nên đồ thị này.Ánh xạ một-một giữa x và y (phần gradient đen trắng) được gọi là nội suy tuyến tính (linear interpolation). Ta có thể thay các hàm toán học khác để thay đổi hình dáng của đồ thị. Ví dụ ta có thể vẽ đồ thị luỹ thừa 5 của x để có được đường cong như hình dưới.
<div class="codeAndCanvas" data="expo.frag"></div>
Thú vị phải không ? Ở dòng 22, hãy thử các số mũ khác nhau như: 20.0, 2.0, 1.0, 0.0, 0.2 và 0.02 chẳng hạn. Hiểu được mối quan hệ giữa giá trị và số mũ sẽ rất hữu ích. Bằng cách sử dụng các hàm toán học ở đây bạn sẽ nắm rõ hơn về cách điều khiển các đường cong.
pow() là hàm có sẵn trong số rất nhiều hàm của GLSL. Hầu hết đều được tăng tốc tính toán bởi phần cứng, có nghĩa là nếu được dùng đúng cách nó sẽ giúp code chạy nhanh hơn.exp(), log() và sqrt(). Một số hàm sẽ cho kết quả thú vị nếu bạn sử dụng hằng số PI. Ở dòng 8 tôi có định nghĩa macro sẽ thay thế tất cả những chỗ PI xuất hiện bằng hằng số 3.14159265359.Step và Smoothstep
GLSL cũng có vài hàm số riêng biệt được tăng tốc bởi phần cứng.
step() nhận 2 tham số đầu vào. Tham số đầu tiên là một ngưỡng giới hạn nào đó, còn tham số thứ 2 là giá trị mà ta muốn biết có vượt qua ngưỡng giới hạn kia không. Bất kỳ giá trị nào nhỏ hơn ngưỡng sẽ cho kết quả 0.0 và ngược lại, tất cả các giá trị lớn hơn ngưỡng sẽ cho kết quả 1.0.Hãy thử thay giá trị ở dòng 20 của đoạn code dưới đây.
<div class="codeAndCanvas" data="step.frag"></div>
smoothstep(). Tham số đầu vào là 1 khoảng min-max kèm thêm 1 giá trị. Hàm này sẽ nội suy giá trị đó trong khoảng min-max, các giá trị nằm ngoài khoảng này sẽ trở min hoặc max tuỳ theo nó nằm ở phía nào của khoảng đã cho.<div class="codeAndCanvas" data="smoothstep.frag"></div>
Ở ví dụ trước, dòng 12, chú ý rằng ta đã dùng hàm
smoothstep để vẽ đồ thị trong hàm plot(). Nếu muốn đồ thị trồi lên ở một đoạn nào đó thì làm thế nào ? Bằng cách ghép hai hàm smoothstep() lại. Hãy thay dòng code dưới đây vào dòng 20. Trông như ta đã chẻ đôi canvas ra phải không ?glslfloat y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sin và Cos
Khi bạn muốn dùng Toán để tạo chuyển động, tạo hình hay pha trộn các giá trị, không có gì tốt hơn việc làm quen với sin và cos.
Hai hàm lượng giác cơ bản này kết hợp với nhau để tạo nên những vòng tròn đa năng như dao gấp quân đội Thuỵ Sỹ của MacGyver vậy. Việc tìm hiểu cách chúng hoạt động và kết hợp với nhau ra sao rất quan trọng. Về cơ bản, cho một góc bất kỳ (đơn vị radian), hai hàm này sẽ cho kết quả là tọa độ x (cos) và y (sin) của 1 điểm trên đường tròn có bán kính bằng 1. Và chính việc kết quả thu được từ 2 hàm này vừa biến thiên một cách mềm mại lại còn luôn được chuẩn hoá sẵn theo cặp và cả đơn lẻ (trong khoảng -1 tới 1) khiến cho 2 hàm này trở thành các công cụ siêu hữu ích.
Mặc dù rất khó để mô tả mối liên hệ giữa các hàm lượng giác với đường tròn, nhưng chuyển động đẹp tuyệt trên đây đã làm rất tốt nhiệm vụ mô tả tóm tắt mối liên hệ này.
<div class="simpleFunction" data="y = sin(x);"></div>
Hãy nhìn thật kỹ đồ thị hình sine. Chú ý cách mà các giá trị y biến thiên rất mượt giữa +1 và -1. Như ta đã thấy ở ví dụ có sử dụng thời gian ở chương trước, bạn có thể sử dụng tính chất tuần hoàn này của hàm
sin() để áp dụng cho các thuộc tính. Nếu bạn đang đọc trên trình duyệt, bạn có thể sửa đoạn code phía trên để xem các sóng đồ thị thay đổi như thế nào. (Chú ý: Đừng quên dấu chấm phẩy ở cuối dòng.)Hãy thử các thay đổi sau và xem điều gì xảy ra:
Cộng
u_time với x* trước khi gọi hàm sin. Bạn sẽ thấy sóng đồ thị sẽ dịch chuyển dọc theo trục hoành. Nhân x* với
PI trước khi gọi hàm sin. Bạn sẽ thấy 2 chu kỳ bị co lại và lặp lại mỗi 2 đơn vị số nguyên. Nhân
u_time với x* trước khi gọi hàm sin. Bạn sẽ thấy tần số (frequency) giữa các chu kỳ ngày càng ngắn lại. Chú ý rằng u_time càng lớn thì càng khó nhìn rõ đồ thị do các chu kỳ bị co lại rất nhiều.sin(x). Và bạn sẽ thấy toàn bộ sóng được nâng lên (displaced) khiến cho giá trị nằm trong khoảng 0.0 và 2.0.sin(x) với 2.0. Và bạn sẽ thấy biên độ (amplitude) rộng gấp đôi.abs()) của hàm sin(x). Trông đồ thị sẽ giống như đường đi của quả bóng nảy trên mặt đất.fract() từ kết quả của sin(x).ceil() và làm tròn xuống bằng hàm floor() từ kết quả của sin(x) để có được sóng điện tử của các giá trị 1 và -1.Các hàm hữu ích khác
Chúng tôi vừa mới giới thiệu cho các bạn 1 vài hàm mới. Giờ là lúc thử nghiệm từng hàm một bằng cách uncomment từng dòng dưới đây một. Hãy làm quen với các hàm này. Tôi biết bạn đang thắc mắc... tại sao ? Google nhanh với từ khoá "generative art" sẽ cho bạn câu trả lời. Hãy nhớ rằng các hàm này là hàng rào của chúng ta. Chúng ta đang dần thuần thục với các chuyển động 1 chiều, chỉ có lên và xuống. Sớm thôi, ta sẽ đụng tới các chiều thứ hai, ba và bốn!
<div class="simpleFunction" data="y = mod(x,0.5); // tính phần dư của phép tính x / 0.5
//y = fract(x); // tách phần thập phân của x
//y = ceil(x); // làm tròn lên
//y = floor(x); // làm tròn xuống
//y = sign(x); // lấy dấu âm dương của x
//y = abs(x); // tính giá trị tuyệt đối của x
//y = clamp(x,0.0,1.0); // kẹp x trong khoảng 0.0 và 1.0
//y = min(0.0,x); // tìm số nhỏ nhất giữa 2 số 0.0 và x
//y = max(0.0,x); // tìm số lớn nhất giữa 2 số 0.0 và x "></div>
Các hàm nâng cao
Golan Levin có tài liệu mô tả rất chi tiết về các hàm phức tạp khác vô cùng hữu ích. Ứng dụng chúng vào GLSL sẽ là một bước đi thông minh để bắt đầu dựng nên thư viện code của chính bạn.
* Các hàm đa thức: www.flong.com/archive/texts/code/shapers_poly
* Các hàm luỹ thừa: www.flong.com/archive/texts/code/shapers_exp
* Các hàm mô phỏng đường tròn và elip: www.flong.com/archive/texts/code/shapers_circ
* Đường cong Bezier và các hàm tương tự: www.flong.com/archive/texts/code/shapers_bez
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Như một đầu bếp đi thu thập các kỳ hoa dị thảo, nghệ sỹ kỹ thuật số và các lập trình viên đồ hoạ cũng sẽ có niềm yêu thích riêng với các hàm nội suy của riêng họ.
Iñigo Quiles có 1 bộ sưu tầm các hàm rất hữu ích. Sau khi đọc bài báo này hãy xem cách thực thi các hàm đó trong GLSL. Hãy chú ý tới các tiểu tiết như thêm dấu chấm "." vào sau các số thực và sử dụng cách đặt tên hàm của ngôn ngữ C vào GLSL; ví dụ thay vì
powf() hãy dùng pow(): <div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Để tạo động lực cho bạn, đây là 1 ví dụ hoàn hảo (tạo nên bởi Danguafer) về việc thuần thục môn võ karate-hàm-số.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
Ở các chương tiếp theo (Next >>) chúng ta sẽ học các chiêu mới. Đầu tiên là trộn màu rồi sau đó là vẽ hình.
#### Bài tập
Hãy nhìn vào bảng các phương trình dưới đây, được tạo bởi Kynd. Hãy xem cách mà anh ấy kết hợp các hàm lại với nhau để kiểm soát các giá trị nằm trong khoảng 0.0 và 1.0. Giờ là lúc bạn tập luyện bằng cách dựng lại các hàm này. Hãy nhớ rằng càng luyện tập chăm chỉ bạn sẽ càng giỏi võ.
#### Một vài công cụ cho bạn
Đây là một vài công cụ sẽ giúp bạn vẽ đồ thị các hàm 1 cách trực quan nhất.
* GraphToy: Thêm một sản phẩm nữa của Iñigo Quilez để minh hoạ các hàm GLSL trên WebGL.
* LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
---
05/SUMMARY (05/SUMMARY.md)
Shaping functions is fundamental technique that is recursively used throughout this book that let you control the variation of the value at will. Study how different functions of x are used to create different shapes and try making your own function.
---
06/README (06/README.md)
Colors
We haven't had much of a chance to talk about GLSL vector types. Before going further it's important to learn more about these variables and the subject of colors is a great way to find out more about them.
If you are familiar with object oriented programming paradigms you've probably noticed that we have been accessing the data inside the vectors like any regular C-like
struct.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Defining color using an x, y and z notation can be confusing and misleading, right? That's why there are other ways to access this same information, but with different names. The values of
.x, .y and .z can also be called .r, .g and .b, and .s, .t and .p. (.s, .t and .p are usually used for spatial coordinates of a texture, which we'll see in a later chapter.) You can also access the data in a vector by using the index position, [0], [1] and [2].The following lines show all the ways to access the same data:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
These different ways of pointing to the variables inside a vector are just nomenclatures designed to help you write clear code. This flexibility embedded in shading language is a door for you to start thinking interchangeably about color and space coordinates.
Another great feature of vector types in GLSL is that the properties can be combined in any order you want, which makes it easy to cast and mix values. This ability is called swizzle.
glslvec3 yellow, magenta, green;// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
Mixing color
Now that you know how colors are defined, it's time to integrate this with our previous knowledge. In GLSL there is a very useful function,
mix(), that lets you mix two values in percentages. Can you guess what the percentage range is? Yes, values between 0.0 and 1.0! Which is perfect for you, after those long hours practicing your karate moves with the fence - it is time to use them!Check the following code at line 18 and see how we are using the absolute values of a sin wave over time to mix
colorA and colorB.<div class="codeAndCanvas" data="mix.frag"></div>
Show off your skills by:
* Make an expressive transition between colors. Think of a particular emotion. What color seems most representative of it? How does it appear? How does it fade away? Think of another emotion and the matching color for it. Change the beginning and ending color of the above code to match those emotions. Then animate the transition using shaping functions. Robert Penner developed a series of popular shaping functions for computer animation known as easing functions, you can use this example as research and inspiration but the best result will come from making your own transitions.
Playing with gradients
mix() function has more to offer. Instead of a single float, we can pass a variable type that matches the two first arguments, in our case a vec3. By doing that we gain control over the mixing percentages of each individual color channel, r, g and b.Take a look at the following example. Like the examples in the previous chapter, we are hooking the transition to the normalized x coordinate and visualizing it with a line. Right now all the channels go along the same line.
Now, uncomment line number 25 and watch what happens. Then try uncommenting lines 26 and 27. Remember that the lines visualize the amount of
colorA and colorB to mix per channel.<div class="codeAndCanvas" data="gradient.frag"></div>
You probably recognize the three shaping functions we are using on lines 25 to 27. Play with them! It's time for you to explore and show off your skills from the previous chapter and make interesting gradients. Try the following exercises:
* Compose a gradient that resembles a William Turner sunset
* Animate a transition between a sunrise and sunset using
u_time.* Can you make a rainbow using what we have learned so far?
* Use the
step() function to create a colorful flag.HSB
We can't talk about color without speaking about color space. As you probably know there are different ways to organize color besides by red, green and blue channels.
HSB stands for Hue, Saturation and Brightness (or Value) and is a more intuitive and useful organization of colors. Take a moment to read the
rgb2hsv() and hsv2rgb() functions in the following code.By mapping the position on the x axis to the Hue and the position on the y axis to the Brightness, we obtain a nice spectrum of visible colors. This spatial distribution of color can be very handy; it's more intuitive to pick a color with HSB than with RGB.
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB in polar coordinates
HSB was originally designed to be represented in polar coordinates (based on the angle and radius) instead of cartesian coordinates (based on x and y). To map our HSB function to polar coordinates we need to obtain the angle and distance from the center of the billboard to the pixel coordinate. For that we will use the
length() function and atan(y,x) (which is the GLSL version of the commonly used atan2(y,x)).When using vector and trigonometric functions,
vec2, vec3 and vec4 are treated as vectors even when they represent colors. We will start treating colors and vectors similarly, in fact you will come to find this conceptual flexibility very empowering.Note: If you were wondering, there are more geometric functions besides
length like: distance(), dot(), cross, normalize(), faceforward(), reflect() and refract(). Also GLSL has special vector relational functions such as: lessThan(), lessThanEqual(), greaterThan(), greaterThanEqual(), equal() and notEqual().Once we obtain the angle and length we need to “normalize” their values to the range between 0.0 to 1.0. On line 27,
atan(y,x) will return an angle in radians between -PI and PI (-3.14 to 3.14), so we need to divide this number by TWO_PI (defined at the top of the code) to get values between -0.5 to 0.5, which by simple addition we change to the desired range of 0.0 to 1.0. The radius will return a maximum of 0.5 (because we are calculating the distance from the center of the viewport) so we need to double this range (by multiplying by two) to get a maximum of 1.0.As you can see, our game here is all about transforming and mapping ranges to the 0.0 to 1.0 that we like.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Try the following exercises:
* Modify the polar example to get a spinning color wheel, just like the waiting mouse icon.
* Use a shaping function together with the conversion function from HSB to RGB to expand a particular hue value and shrink the rest.
* If you look closely at the color wheel used on color pickers (see the image below), they use a different spectrum according to RYB color space. For example, the opposite color of red should be green, but in our example it is cyan. Can you find a way to fix that in order to look exactly like the following image? [Hint: this is a great moment to use shaping functions.]
* Read Josef Albers' book Interaction of Color and use the following shaders examples as practice.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Note about functions and arguments
Before jumping to the next chapter let’s stop and rewind. Go back and take look at the functions in previous examples. You will notice
in before the type of the arguments. This is a qualifier and in this case it specifies that the variable is read only. In future examples we will see that it is also possible to define arguments as out or inout. This last one, inout, is conceptually similar to passing an argument by reference which will give us the possibility to modify a passed variable.glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
You may not believe it but now we have all the elements to make cool drawings. In the next chapter we will learn how to combine all our tricks to make geometric forms by blending the space. Yep... blending the space.
#### For your toolbox
* LYGIA's color shader functions are set of reusable functions to manipulate colors in GLSL. It includes functions to convert between color spaces, to blend colors, to create gradients, and to apply color transformations. It's very granular library, designed for reusability, performance and flexibility. And it can be easily be added to any projects and frameworks.
---
06/Notes (06/notes.md)
Add some references and ideas from this IQ article: http://iquilezles.org/www/articles/palettes/palettes.htm
### nicolas
* struct exist in GLSL : this is valid
`precision mediump float;
struct my_struct {
vec4 color;
};
uniform my_struct u_colors[2];
void main(void) {
gl_FragColor = u_colors[0].color;
}
textsource
[NICO]
note: I had previously associated the name accessor to 'the way one can access the properties of an object'.
we can use those accessors independently ; the following code creates a clone newColor of the color vector by using a different accessor for each property.
glsl
vec4 color = vec4( 1.,0.,0.5,1. );
vec4 newColor = vec4( color[0], color.g, color.z, color.q );
textIt is possible to combine the properties by concatenating different accessors:
if we need to use the
.r`, `.g` and `.b` values of a 4 dimensions vector but don't need the `.a` (alpha) value, we can write:glslvec4 color = vec4( 1.,0.,0.5,1. );
vec4 newColor = vec4( color.rgb, 1.0 );
Which is the same as cloning each property
`.r`, `.g` and `.b` from `color` individually and dropping the last (`.a`).In this case,
`color.rgb` is interpreted as a vector of type `vec3` that contains the values `.r`, `.g` and `.b` of the original `vec4` vector color.
The same goes for:glslvec4 color = vec4( 1.,0.,0.5,1. );
vec3 newColor = vec3( color.xy, 1.0 );
We use the values
`.x` and `.y` from color to build a vector newColors of type `vec3` which `.r` and `.g` values will be the same as the color vector and `.b` value will be `1.0`.Last but not least, the order in which you concatenate the accessors matters.
If you want to build a vector from another vector but want to reverse the order of the properties, here's how you can write it:
glslvec3 color = vec3( 1.0, 0.0, 0.5 );
vec3 newColor = color.bgr;
the newColor vector will copy color's properties but instead of copying them in the "regular" order:
`.r`, `.g` and `.b`,
it will copy them in the order defined by the concatenation: `.b`, `.g` and `.r`.glslcolor.r => 1.0
color.g => 0.0
color.b => 0.5
and
newColor.r => 0.5
newColor.g => 0.0
newColor.b => 1.0
On a side note, you can reuse the same accessor mutiple times and in whatever order in a concatenated accessor:
glslcolor.rrr => vec3( 1.0 )
color.rrg => vec3( 1.0, 1.0, 0.0)
color.bgg => vec3( 0.5, 0.0, 0.0)
etc.
This results in the fact that, if the following is true:
glslcolor.rgba = color.xyzw = color.stpq
these statements are not necessarily true:
glslcolor.rgba != color.argb != color.rbga != color.abgr etc.
color.xyzw != color.wxyz != color.xzyw != color.wzyx etc.
color.stpq != color.qstp != color.sptq != color.qpts etc.
This is a powerful feature ; it allows to store the data in compact forms and manipulate them in very flexible ways.
Let's see a use case of the compactness: say you want to describe a rectangle, you can do so by using 2
`vec2` describing respectively the top left corner and the bottom right corner
or you can instead, use a single `vec4` which `.xy` accessor will return a `vec2` describing the top left corner, and which `.zw` accessor will return a `vec2` describing the bottom right corner.[/NICO]
These different ways of pointing to the variables inside a vector are just nomenclatures designed to help you write clear code. This flexibility embedded in shading language is a door for you to start thinking interchangably about color and space coordinates.
[NICO]
rephrased this:
Another great feature of vector types in GLSL is that the properties can be combined in any order you want, which makes it easy to cast and mix values. This ability is called swizzle.
to:
Concatenation or swizzle gets really interesting when we need to cast and mix values. The following example show you how to swizzle properties between vectors.
[/NICO]
---
06/README Ch (06/README-ch.md)
颜色
我们目前为止还未涉及到GLSL的向量类型。在我们深入向量之前,学习更多关于变量和色彩主题是一个了解向量类型的好方法。
若你熟悉面向对象的编程范式(或者说编程思维模式),你一定注意到我们以一种类C的
`struct`的方式访问向量数据的内部分量。
glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
以x,y,z定义颜色是不是有些奇怪?正因如此,我们有其他方法访问这些变量——以不同的名字。
`.x`, `.y`, `.z`也可以被写作`.r`, `.g`, `.b` 和 `.s`, `.t`, `.p`。(`.s`, `.t`, `.p`通常被用做后面章节提到的贴图空间坐标)你也可以通过使用索引位置`[0]`, `[1]` 和 `[2]`来访问向量.下面的代码展示了所有访问相同数据的方式:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
这些指向向量内部变量的不同方式仅仅是设计用来帮助你写出干净代码的术语。着色语言所包含的灵活性为你互换地思考颜色和坐标位置。
GLSL中向量类型的另一大特点是可以用你需要的任意顺序简单地投射和混合(变量)值。这种能力被(形象地)称为:鸡尾酒。
glslvec3 yellow, magenta, green;// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
混合颜色
`mix()`,这个函数让你以百分比混合两个值。猜下百分比的取值范围?没错,0到1!完美!学了这么久的基本功,是时候来用一用了!看下下列代码中的第18行,这里展示了我们如果是用随时间变化的sin绝对值来混合
`colorA` 和 `colorB`。<div class="codeAndCanvas" data="mix.frag"></div>
试着来 show 一下你所学到的:
* 给颜色赋予一个有趣的过渡。想想某种特定的感情。哪种颜色更具代表性?他如何产生?又如何褪去?再想想另外的一种感情以及对应的颜色。然后改变上诉代码中的代表这种情感的开始颜色和结束颜色。Robert Penner 开发了一些列流行的计算机动画塑形函数,被称为缓动函数。你可以研究这些例子并得到启发,但最好你还是自己写一个自己的缓动函数。
玩玩渐变
`mix()` 函数有更多的用处。我们可以输入两个互相匹配的变量类型而不仅仅是单独的 `float` 变量,在我们这个例子中用的是 `vec3`。这样我们便获得了混合颜色单独通道 `.r`,`.g` 和 `.b`的能力。试试下面的例子。正如前面一个例子,我们用一条线来可视化根据单位化x坐标的过渡。现在所有通道都按照同样的线性变换过渡。
现在试试取消25行的注释,看看会发生什么。然后再试试取消26行和27行。记住直线代表了
`colorA` 和 `colorB`每个通道的混合比例。<div class="codeAndCanvas" data="gradient.frag"></div>
你可能认出了我们用在25行到27行的造型函数。试着改写他们!是时候把前几张的内容结合起来探索一些新的渐变。试试下列挑战:
* 创作一个渐变来代表 William Turner的落日。
* 用
`u_time` 做个一日出和日落的动画。* 能用我们所学的做一道彩虹吗?
* 用
`step()` 函数在做一个五彩的旗子。HSB
我们不能脱离色彩空间来谈论颜色。正如你所知,除了rgb值,有其他不同的方法去描述定义颜色。
HSB 代表色相,饱和度和亮度(或称为值)。这更符合直觉也更有利于组织颜色。稍微花些时间阅读下面的
`rgb2hsv()` 和 `hsv2rgb()` 函数。将x坐标(位置)映射到Hue值并将y坐标映射到明度,我们就得到了五彩的可见光光谱。这样的色彩空间分布实现起来非常方便,比起RGB,用HSB来拾取颜色更直观。
<div class="codeAndCanvas" data="hsb.frag"></div>
极坐标下的HSB
HSB原本是在极坐标下产生的(以半径和角度定义)而并非在笛卡尔坐标系(基于xy定义)下。将HSB映射到极坐标我们需要取得角度和到像素屏中点的距离。由此我们运用
`length()` 函数和 `atan(y,x)` 函数(在GLSL中通常用atan(y,x))。当用到矢量和三角学函数时,
`vec2`, `vec3` 和 `vec4`被当做向量对待,即使有时候他们代表颜色。我们开始把颜色和向量同等的对待,事实上你会慢慢发现这种理念的灵活性有着相当强大的用途。`distance()`, `dot()`, `cross`, `normalize()`, `faceforward()`, `reflect()` 和 `refract()`。 GLSL也有与向量相关的函数:`lessThan()`, `lessThanEqual()`, `greaterThan()`, `greaterThanEqual()`, `equal()` and `notEqual()`。`atan(y,x)` 会返回一个介于-PI到PI的弧度值(-3.14 to 3.14),所以我们要将这个返回值除以 `TWO_PI`(在code顶部定义了)来得到一个-0.5到0.5的值。这样一来,用简单的加法就可以把这个返回值最终映射到0.0到1.0。半径会返回一个最大值0.5(因为我们计算的是到视口中心的距离,而视口中心的范围已经被映射到0.0到1.0),所以我们需要把这个值乘以二来得到一个0到1.0的映射。正如你所见,这里我们的游戏都是关于变换和映射到一个0到1这样我们乐于处理的值。
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
来挑战下下面的练习吧:
* 把极坐标映射的例子改成选择色轮,就像“正忙”的鼠标图标。
* 把造型函数整合进来,来让HSB和RGB的转换中强调某些特定值并且弱化其他的。
* 如果你仔细观察用来拾色的色轮(见下图),你会发现它用一种根据RYB色彩空间的色谱。例如,红色的对面应该是绿色,但在我们的例子里是青色。你能找到一种修复的方式来让它看起来和下图一样么?[提示:这是用塑形函数的好机会!]
#### 注意函数和变量
在进入下一章之前让我们停下脚步回顾下。复习下之前例子的函数。你会注意到变量类型之前有个限定符
`in`,在这个 qualifier (限定符)例子中它特指这个变量是只读的。在之后的例子中我们会看到可以定义一个 `out` 或者 `inout`变量。最后这个 `inout`,再概念上类似于参照输入一个变量,这意味着我们有可能修改一个传入的变量。glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
或许你还不相信我们可以用所有这些元素来画一些炫酷的东西。下一章我们会学习如何结合所有这些技巧通过融合 (blending) 空间来创造几何形状。没错。。。融合(blending) 空间。
---
06/README De (06/README-de.md)
Farben
Wir hatten bislang noch wenig Gelegenheit, um über die Vektortypen von GLSL zu sprechen. Bevor es mit anderen Inhalten weitergeht, ist es wichtig, mehr über diese Variablentypen zu erfahren. Das Thema „Farben“ bietet sich dafür an.
Falls Du mit den Konzepten der objektorientierten Programmierung vertraut bist, ist Dir vielleicht schon aufgefallen, dass wir die verschiedenen Elemente innerhalb eines Vektors wie eine gewöhnliche
`struct` in C ansprechen.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Die Festlegung von Farben über die Komponenten x, y und z wirkt auf den ersten Blick etwas merkwürdig, nicht wahr? Aus diesem Grund gibt es weitere Möglichkeiten, um auf diese Elemente zuzugreifen. Die Inhalte von
`.x`, `.y` und `.z` können auch als `.r`, `.g` und `.b`, sowie als `.s`, `.t` und `.p` angesprochen werden (`.s`, `.t` und `.p` werden typischerweise für die Raumkoordinaten von Texturen genutzt, wie wir in späteren Kapiteln noch sehen werden). Darüber hinaus lassen sich die Elemente eines Vektors auch über ihre Index-Position als `[0]`, `[1]` und `[2]` ansprechen.Die folgenden Programmzeilen zeigen die unterschiedlichen Ansätze, um jeweils auf die gleichen Vektorinhalte zuzugreifen:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Tatsächlich handelt es sich bei den unterschiedlichen Namen nur um verschiedene Bezeichner für jeweils ein und dieselbe Sache. Die Namen sollen Dir helfen, verständlichen Code zu schreiben, je nachdem, wofür ein Vektor gerade zum Einsatz kommt (Farben, Koordinaten, Raumpunkte etc.)
Ein weiteres praktisches Merkmal der Vektortypen in GLSL ist die Möglichkeit, ihre Eigenschaften in beliebiger Weise zu kombinieren. Das macht es besonders einfach, Werte zu tauschen und in andere Datentypen zu konvertieren. Diese Fähigkeit wird im Englischen als swizzle bezeichnet, was so viel wie „umrühren“ oder „mischen“ bedeutet.
glslvec3 yellow, magenta, green;// Zusammenruehren von Gelb
yellow.rg = vec2(1.0); // Zuweisung von 1. an den Rot- und den Gruen-Kanal von yellow
yellow[2] = 0.0; // Zuweisung von 0. an den Blau-Kanal von yellow
// Zusammenruehren von Magenta
magenta = yellow.rbg; // Zuweisung von yellow an magenta bei gleichzeitigem Tausch der Kanaele fuer Blau und Gruen
// Zusammenruehren von Gruen
green.rgb = yellow.bgb; // Zuweisung des Blau-Kanals von yellow an den Rot- und Blau-Kanal von green
Mischen von Farben
Jetzt, wo Du weißt, wie man Farben definiert, wird es Zeit, dies mit unserem bisher gesammelten Wissen zu verknüpfen. In GLSL gibt es eine äußerst praktische Funktion mit dem Namen
`mix()`, über die man zwei Werte in Form von Prozentangaben mischen kann. Vielleicht kannst Du Dir bereits denken, wie diese Prozentangaben auszusehen haben? Genau, als Werte zwischen `0.0` und `1.0`! Das passt doch perfekt, nachdem Du schon so viel Zahlen-Karate am Zaun geübt hast. Es ist an der Zeit, Dein Wissen umzusetzen.Lenke Dein Augenmerk im folgenden Programm besonders auf die Zeile 18. Schau Dir genau an, wie hier die absoluten Werte aus einer Sinusfunktion genutzt werden, um zeitabhängig und mit unterschiedlichen Verhältnissen die Farben aus den Variablen
`colorA` und `colorB` zu mischen.<div class="codeAndCanvas" data="mix.frag"></div>
Jetzt zeige Deine Fähigkeiten, indem Du:
* Einen ausdrucksstarken Übergang zwischen den Farben konstruierst. Denke an ein bestimmtes Gefühl, eine impulsive menschliche Regung. Welche Farbe vermag dieses Gefühl wohl am besten auszudrücken? Und wie soll sich diese Farbe entwickeln, um anschließend wieder zu verschwinden? Animiere den Übergang mit Hilfe formgebender Funktionen. Robert Penner hat eine Reihe populärer Übergangsfunktionen für Computeranimationen entwickelt, die als easing functions bekannt sind. Du kannst für Deine Nachforschungen und als Inspiration auf dieses Beispiel zurückgreifen. Aber die besten Ergebnisse erzielst Du natürlich, wenn Du Deine ganz eigenen Übergänge kreierst.
Das Spiel mit Farbverläufen
`mix()`-Funktion hat noch mehr zu bieten. Anstelle eines einzelnen Werts vom Typ `float`, können wir auch einen Datentyp übergeben, der zu den ersten beiden Argumenten passt. In unserem Fall ist das ein `vec3`. Dadurch gewinnen wir die Kontrolle über das Mischen in allen drei Farbkanälen Rot, Grün und Blau (`r`, `g` und `b`).Wirf nun einen Blick auf das folgende Beispiel. Wie schon bei den Beispielen im letzten Kapitel verbinden wir den Übergang auch hier mit dem normalisierten Wert der X-Ordinate und visualisieren ihn als eine Linie. Im ersten Schritt folgen die Übergänge in allen drei Farbkanälen derselben Linie.
Lösche jetzt die Kommentarzeichen aus der Programmzeile 25, damit diese ebenfalls ausgeführt wird. Dann schau, was daraufhin geschieht. Entferne anschließend auch die Kommentarzeichen vor den Zeilen 26 und 27. Achte darauf, dass diese drei Zeilen jeweils das Mischverhältnis für die Rot-, Grün, und Blau-Kanäle zwischen den Farben aus den Variablen
`colorA` und `colorB` festlegen.<div class="codeAndCanvas" data="gradient.frag"></div>
Vielleicht erkennst Du die drei formgebenden Funktionen in den Zeilen 25 bis 27 wieder. Experimentiere mit ihnen. Es ist an der Zeit, dass Du die erlernten Fähigkeiten aus dem letzten Kapitel nutzt, um interessante Farbverläufe zu produzieren. Probiere die folgenden Übungen aus:
* Erzeuge einen Farbverlauf, der an den Sonnenuntergang bei William Turner erinnert.
* Animiere einen Übergang zwischen Sonnenaufgang und Sonnenuntergang mit Hilfe von
`u_time`.* Kannst Du mit Hilfe des bislang Erlernten einen Regenbogen entstehen lassen?
* Nutze die
`step()`-Funktion, um eine farbenfrohe Flagge zu erzeugen.HSB
Beim Thema „Farben“ kommen wir nicht an dem Konzept der „Farbräume“ vorbei. Wie Du vielleicht weißt, gibt es unterschiedliche Möglichkeiten, Farben zu beschreiben, jenseits ihrer Auftrennung in Rot-, Grün- und Blau-Anteile (sprich: Kanäle).
HSB steht für Hue (dt. Farbwert), Saturation (dt. Farbsättigung) und Brightness (dt. absolute Helligkeit). Dieses Farbsystem ist intuitiver und in vielen Fällen auch praktischer, wenn es um die Festlegung von Farben geht. Nimm Dir einen Moment Zeit, um die Konvertierungsfunktionen
`rgb2hsv()` und `hsv2rgb()` im folgenden Programmcode zu studieren.Indem wir die Position auf der X-Achse auf den Farbwert und die Position auf der Y-Achse auf die Helligkeit abbilden, erhalten wir ein hübsches Spektralbild. Diese räumliche Verteilung der Farben kann sehr praktisch sei, wenn es um die Auswahl einer Farbe für einen bestimmten Zweck geht.
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB in Polarkoordinaten
Das HSB-Farbmodell wurde ursprünglich entwickelt, um Farben in Polarkoordinaten (bestehend aus einem Winkel und einem Radius) auszudrücken und nicht als kartesische Koordinaten (bestehend aus einer X- und einer Y-Ordinate). Um unsere
`HSB`-Funktion mit Polarkoordinaten arbeiten zu lassen, müssen wir den Winkel und die Entfernung des jeweiligen Bildpunktes von der Mitte der Zeichenfläche berechnen. Dafür nutzen wir die `length()`-Funktion, sowie die Funktion `atan(y,x)` (das ist die GLSL-Variante der in vielen Programmiersprachen verfügbaren Funktion `atan2(y,x)` zur Berechnung des Arkustangens). Bei der Nutzung von Vektor- und Trigonometrie-Funktionen werden Variablen der Datentypen
`vec2`, `vec3` und `vec4` wie Vektoren behandelt, auch wenn sie tatsächlich Farben verkörpern. Wir beginnen hier also, Farben und Vektoren gleichermaßen zu bearbeiten - eine Flexibilität, die sich noch als äußerst praktisch und weitreichend erweisen wird.`length` gibt es noch viele weitere geometrische Funktionen. Dazu gehören beisielsweise: `distance()`, `dot()`, `cross`, `normalize()`, `faceforward()`, `reflect()` und `refract()`.Außerdem bietet GLSL vergleichende Funktionen für Vektoren wie
`lessThan()`, `lessThanEqual()`, `greaterThan()`, `greaterThanEqual()`, `equal()` und `notEqual()`.Nachdem wir den Winkel und die Entfernung (Länge) berechnet haben, müssen wir diese Werte normalisieren, indem wir sie auf den Wertebereich zwischen
`0.0` und `1.0` abbilden. In der Programmzeile 27 liefert der Aufruf von `atan(y,x)` den Winkel als Bogenmaß zwischen -PI und PI (`-3.14` bis `3.14`) zurück. Deshalb müssen wir dieses Ergebnis durch `TWO_PI` (Zweimal PI, als Konstante oben im Programm definiert) teilen. Wir erhalten dadurch Werte zwischen `-0.5` und `0.5`, die wir durch einfache Addition von `0.5` auf den benötigten Wertebereich zwischen `0.0` und `1.0` abbilden. Allerdings werden wir hier als Ergebnis immer maximal `0.5` erhalten, weil wir ja die Entfernung von der Mitte der Zeichenfläche berechnen. Deshalb müssen wir dieses Ergebnis noch mit `2` multiplizieren, damit wir maximal auf den Wert von `1.0` kommen.Wie Du siehst, dreht sich also auch hier das ganze Spiel darum, Werte zwischen
`0.0` und `1.0` zu erzielen, mit denen wir so gerne arbeiten.<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Probiere die folgenden Übungen aus:
* Verändere das obige Programmbeispiel so, dass sich das Farbrad dreht, wie der Mauszeiger bei einer länger währenden Operation.
Nutze eine formgebende Funktion in Verbindung mit der Konvertierungsfunktion von HSB nach RGB*, um einen bestimmten Farbwert in den Vordergrund zu rücken und die anderen Farben „klein“ zu halten.
Wenn Du Dir das Farbrad auf Farbauswahlfeldern (wie in der folgenden Abbildung) genau anschaust, erkennst Du, dass diese einen RYB-Farbraum* repräsentieren. Die gegenüberliegende Farbe von Rot sollte z.B. Grün sein, doch in unserem obigen Beispielprogramm erscheint dort Zyan. Gelingt es Dir, einen Weg zu finden, damit unser Beispielprogramm das gleiche Farbbild liefert, wie auf der Abbildung unten? (Ein Tipp: Das ist der perfekte Moment, um eine passende formgebende Funktion zum Einsatz zu bringen.)
* Lies Josef Albers Buch: „Interaction of Color“ und klicke die folgenden Shader an, um aus deren Programmcode zu lernen.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Ein Hinweis zu Funktionen und ihren Argumenten
Bevor wir zum nächsten Kapitel springen, lass und kurz innehalten und einen Schritt zurückgehen. Schau Dir noch einmal die Funktionen aus den letzten Beispielprogrammen an. Vielleicht fällt Dir das Schüsselwort
`in` im Kopf einer Funktion vor dem jeweiligen Argument auf. Es handelt sich dabei um einen sogenannten qualifier, der in diesem Fall festlegt, dass der jeweilige Parameter von der Funktion nur ausgelesen und nicht überschrieben werden kann. In kommenden Programmbeispielen werden wir sehen, dass Parameter auch als `out` oder `inout` gekennzeichnet werden können. `inout` entspricht dabei der Übergabe eines Arguments „by reference“, so dass Änderungen an diesem Parameter auch an den Aufrufer und in die von ihm eingesetzte Variable zurückfließen.glslint newFunction(in vec4 aVec4, // nur auslesbar
out vec3 aVec3, // nicht initalisiert, nur beschreibbar
inout int aInt); // lesen und schreiben, Aenderungen fliessen zum Aufrufer zurueck
Du hast vielleicht nicht damit gerechnet, aber jetzt haben wir bereits alle Elemente beisammen, um aufregende Grafiken zu erstellen. Im nächsten Kapitel werden wir lernen, wie man alle unsere kleinen Tricks nutzen kann, um den Raum richtig in Wallung zu bringen. Ja, du hast richtig gehört. Genau darum geht's.
---
06/README Es (06/README-es.md)
Colores
No hemos tenido oportunidad todavía de hablar sobre los tipos de vectores en GLSL. Antes de ir más lejos, es importante entender cómo funcionan estas variables, hablar de colores es una buena forma de entenderlos.
Si te encuentras familiarizado con la programación orientada a objetos, probablemente te habrás dado cuenta que estamos accediendo a los valores de los vectores, como si fuese un
`struct` de C.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Definir los colores usando x, y y z puede ser muy confuso y engañoso. Es por eso que hay otras formas de acceder a esos valores, con nombres diferentes. Los valores de
`.x`, `.y` y `.z`, se pueden llamar también `.r`, `.g` y `.b`, o también `.s`, `.t` y `.p` (`.s`, `.t` y `.p` son usados para coordenadas espaciales de texturas, lo veremos en otro capítulo). También podemos acceder a un valor de los vectores, usando índices de posición,`[0]`, `[1]` y `[2]`.En las siguientes lineas puedes ver todas las formas de acceder al mismo valor.
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Las diferentes formas de acceder a las variables son sólo nomenclaturas diseñadas para que el código sea más claro. Esta flexibilidad de los shaders, es una puerta de entrada para poder relacionar el color con las coordenadas en el espacio.
Otra estupenda funcionalidad de los vectores en GLSL, es que las propiedades se pueden combinar en el orden que quieras, lo que hace muy sencillo manipular y mezclar valores. Esta habilidad es conocida como swizzle.
glslvec3 yellow, magenta, green;// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
Mezclando color
Ahora que sabes como se definen los colores, es hora de integrar esto con lo que aprendimos previamente. En GLSL hay una función muy útil,
`mix()`, que te permite mezclar dos valores en porcentaje. ¿Puedes adivinar cuál es el rango de porcentaje? ¡Si, valores de 0.0 a 1.0! Lo cual es perfecto para ti, porque luego de esas largas horas practicando tus movimientos de karate en la cerca. ¡Ahora es el momento de usar esos conocimientos!
Echa un vistazo al siguiente código en la línea 18 y observa cómo estamos usando los valores absolutos de una onda sinusoidal para mezclar
`colorA` y `colorB`.<div class="codeAndCanvas" data="mix.frag"></div>
Demuestra tus habilidades:
* Crea una transición expresiva entre dos colores. Piensa en una emoción en particular. ¿Qué color es el más representativo de esa emoción? ¿Cómo aparece? ¿Cómo se desvanece? Piensa en otra emoción y el color que la represente. Cambia los colores del código de arriba por los de tus emociones. Luego anima las transiciones utilizando las funciones de forma. Robert Penner desarrolló una serie de funciones de forma populares, conocidas como easing functions, puedes usar este ejemplo como investigación o inspiración, pero los mejores resultados saldrán cuando crees tus propias transiciones.
Jugando con gradientes
`mix()` tiene mucho para ofrecer. En vez de usar un sólo `float`, podemos pasarle variables en los dos primeros argumentos, en nuestro caso un `vec3`. Haciendo esto podemos controlar los porcentajes de cada canal de color, `r`, `g` y `b`.Mira el siguiente ejemplo, como en los del capítulo anterior, estamos conectando la transición normalizada de la coordenada x y visualizándola con una línea. Ahora todos los canales van por la misma línea.
Ahora descomenta la línea 25 y mira lo que sucede. Luego descomenta las líneas 26 y 27. Recuerda que con las líneas visualizamos la cantidad de
`colorA` y `colorB` a mezclar por cada canal.<div class="codeAndCanvas" data="gradient.frag"></div>
Probablemente reconoces las tres funciones de forma que usamos en las líneas 25 y 27. ¡Juega con ellas! Es hora de mezclar y explorar lo aprendido en el capítulo previo para crear gradientes interesantes. Prueba hacer los próximos ejercicios:
* Crea un gradiente que se parezca a un atardecer de William Turner.
* Crea una transición entre el atardecer y el amanecer usando
`u_time`.* ¿Puedes crear un arcoíris con lo que hemos aprendido hasta ahora?
* Usa la función
`step()` para crear una bandera colorida.HSB
No podemos hablar de color sin mencionar el espacio de color. Probablemente sabes que hay diferentes formas de organizar un color más allá de usar los canales de rojo, verde y azul.
HSB significa Hue (tono), Saturation (saturación) y Brightness (brillo o valor), es una forma útil y más intuitiva de organizar el color. Tomate un momento para leer las funciones
`rgb2hsv()` y `hsv2rgb()` del código siguiente.Conectando la posición del eje x al tono y la posición del eje y al brillo podemos obtener este bonito espectro del color visible. La distribución espacial del color puede sernos muy útil; es más intuitivo seleccionar un color usando HSB que con RGB.
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB en coordenadas polares
HSB fue originalmente diseñado para ser representado en coordenadas polares (basadas en el ángulo y el radio) en vez de coordenadas cartesianas (basadas en x e y). Para asociar nuestra función HSB a coordenadas polares necesitamos obtener el ángulo y la distancia del centro de la ventana. Para hacer eso usaremos la función
`length()` y `atan(y,x)` (que es la versión en GLSL del comunmente usado `atan2(y,x)`).Cuando usamos vectores y funciones trigonométricas,
`vec2`, `vec3` y `vec4` son tratados como vectores, incluso cuando representan colores. Comenzaremos a tratar a los colores y a los vectores de una manera similar, de hecho te darás cuenta que es un concepto muy poderoso y flexible.Nota: Si te preguntabas si había más funciones geométricas además de
`length`: `distance()`, `dot()`, `cross`, `normalize()`, `faceforward()`, `reflect()` y `refract()`. También GLSL tiene funciones relacionadas a los vectores como: `lessThan()`, `lessThanEqual()`, `greaterThan()`, `greaterThanEqual()`, `equal()` y `notEqual()`.Una vez que obtenemos el ángulo y la longitud, necesitamos "normalizar" sus valores al rango de 0.0 a 1.0. En la línea 27,
`atan(y,x)` devolverá el angulo en radianes entre -PI y PI (-3.14 a 3.14), por lo que necesitamos dividir este número por `TWO_PI` (declarado arriba en nuestro código) para obtener valores de -0.5 a 0.5, que con una simple suma podemos transformar al rango deseado de 0.0 a 1.0. El radio devolverá un máximo de 0.5 (porque estamos calculando la distancia desde el centro del viewport) por lo tanto necesitamos duplicar este rango (multiplicándolo por dos) para obtener un máximo de 1.0.Como podrás notar, todo se trata de transformar y manipular rangos de 0.0 a 1.0.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Intenta resolver los próximos ejercicios:
* Modifica el ejemplo polar, para obtener una rueda que gire, como el ícono de cargando.
* Usa una función de forma junto con la conversión de HSB a RGB para expandir un color en particular y encoger el resto.
* Si miras detenidamente el círculo cromático usado en los goteros de color (ver la imagen de abajo), usan un espectro diferente, se trata del RYB. Por ejemplo, el opuesto del color rojo, debería ser el verde, pero en nuestro ejemplo es el cyan. ¿Podrías encontrar la forma de igualarlo para que luzca igual a la siguiente imagen? [Pista: este es un buen momento para usar funciones de forma.]
#### Nota acerca de las funciones y los argumentos
Antes de saltar al próximo capítulo, detengámosnos y retrocedamos. Volvamos a ver las funciones en los ejemplos previos. Notaras el
`in` antes del tipo de argumento. Este es un qualifier y en este caso indica que la variable es de solo lectura. En ejemplos futuros veremos que es posible definir argumentos como `out` o `inout`. Este último, `inout`, es conceptualmente similar a pasar un argumento por referencia, lo que nos da la posibilidad de modificar la variable pasada.glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
Seguramente no me crees, pero ya tenemos todos los elementos necesarios para crear dibujos geniales. En el próximo capítulo aprenderemos a combinar todos nuestros trucos para crear formas geométricas mezclando el espacio. Si... mezclando el espacio.
---
06/README Fa (06/README-fa.md)
رنگ ها
خیلی زمان زیادی برای صحبت در مورد بردار های GLSL نداریم، اما قبل از ادامه دادن، یادگیری آنان مهم است و رنگ ها راهی خوب برای فهمیدن و کسب اطلاعات بیشتر در مورد آنان است.
آگر با برنامه نویسی شی گرا آشنا هستید مشاهده کردید که ما مانند ساختار های استراکت زبان C به مقادیر داخل بردار ها دسترسی داریم.
glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
تعریف مقادیر رنگ با x, y, z میتواند گمراه کننده باشد، برای همین روش های دیگری برای دسترسی به این مقادیر وجود دارند، مقادیر x ,y ,z را میتوان با r ,g ,b هم صدا زد. و حتی s, t, p (معمولا برای مختصات فضای یک تکستچر ازین نام ها استفاده میشود، در فصل بعدی خواهید دید). حتی با مکان مقدار مورد نظرتان هم میتوان از آنان استفاده کرد مثلا [1] , [2], [3].
مقدار های داخل هر خط در مثال زیر با هم برابر هستند:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
این روش های مختلف برای دسترسی به داده ها، برای کمک به شما در نوشتن کد های واضح تر است. این منعطف بودن زبان شیدر ها دریچه ای برای شماست که در مورد مختصات رنگ و فضا تفکر کنید.
از دیگر ویژگی های GLSL این است که میتوان، این مقادیر را به هر ترتیبی استفاده کرد. این ویژگی باعث میشود، مقادیر به راحتی ترکیب شوند.اسم این قابلیت swizzle هست.
glslvec3 yellow, magenta, green;// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
ترکیب رنگ ها
اکنون که میدانید رنگ ها چگونه تعریف میشوند، وقت آن است، از دانش های قبلیمان استفاده کنیم. در GLSL فانکشن مفیدی به نام mix() وجود دارد، که به شما این امکان را میدهد دو مقدار را با درصد خاصی با هم مخلوط کنید. آیا میتوانید حدس بزنید دامنه مقدار چقدر است؟ بله بین 0.0 تا 1.0 . بعد از تمرین های گذشته که آن ها هم با اعداد بین 0 تا 1 مرتبط بودند. وقت آن است دوباره از آنان استفاده کنید.
به خط 18 نگاه بیاندازید که چگونه، از قدر مطلق مقادیر سینوس برا ترکیب رنگ A و B استفاده شده است.
<div class="codeAndCanvas" data="mix.frag"></div>
مهارت های خود را توسط نکته زیر به نمایش بگزارید:
* ِیک طیف رنگی ایجاد کنید. به یک احساس خاص فکر کنید. چه رنگی به ذهنتان میآید؟ چگونه محو و ظاهر میشود؟ به احساسی دیگر و رنگ متناسب با آن فکر کنید. ابتدا و انتهای کد بالا را تغییر دهید تا با آن احساس مطابقت داشته باشد، سپس این طیف را با استفاده از توابع دیگر متحرک کنید. رابرت پنر مجموعه توابع شکل دهی معروفی را برای انیمیشن و متحرک سازی ایجاد کرده است، که به عنوان توابع ease شناخته میشوند easing functions, میتوانید از این مثال برای آموزش و الهام استفاده کنید، اما قطعا بهترین نتیجه، نتیجه کار خودتان است.
بازی با گرادیان ها
mix() قابلیت های بیشتری دارد. به جای یک عدد شناور، میتوان یک متغیر که مطابق دو آرگومان اول باشد را ، به آن منتقل کنیم, در مثال ما یک vector 3 هست. با این کار قابلیت کنترل هر یک از کانال های r, g و b را داریم.به مثال زیر نگاه بیاندازید. مانند مثال های فصل قبل، ما مختصات x را به صورت نرمالایز شده استفاده و آن را با یک خط نشان میدهیم. در حال حاضر تمام کانال ها از آن خط عبور میکنند. حال خط 25 را از حالت کامنت خارج کنید و تغییرات را مشاهده کنید، برای خط های 26 و 27 هم نیز همین کار را انجام دهید. یادتان باشد مقدار خط، نشان دهنده ترکیب رنگ های A,B بر حسب هر کانال است.
<div class="codeAndCanvas" data="gradient.frag"></div>
احتمالا سه تابع شکل دهی را در خطوط 25 تا 27 تشخیص داده اید. با آنها بازی کنید. وقت آن است که مهارت های فصل قبل را کاوش کرده و به نمایش بگذارید، سعی کنید شیب های گوناگون و جالب تری ایجاد کنید. تمرینات زیر را امتحان کنید:
* گرادیانی شبیه غروب خورشید ویلیام ترنر بسازید.
* با استفاده از
u_time انتقال بین طلوع و غروب را متحرک کنید.* آیا با استفاده از آنچه تا کنون یاد گرفته اید، میتوانید رنگین کمان بسازید؟
* از تابع
step() برای ساخت پرچم های رنگا رنگ استفاده کنید.HSB
برای آشنایی بیشتر با رنگ ها، با فضای رنگ ها باید آشنا شویم. همانطور که میدانید علاوه بر کانال های R, G, B روش های دیگری برای سازماندهی رنگ وجود دارد.
HSB مخفف رنگ، اشباع، و روشنایی است، که یک سازماندهی مفید تر برای رنگ هاست. به توابع rgb2hsv() و hsv2rgb() در کد زیر دقت کنید.
با نگاشت موقعیت x با مقدار Hue(رنگ) و مقدار y به روشنایی، طیف خوبی از رنگ ها قابل مشاهده اند. این نوع توزیع فضایی رنگ میتواند بسیار مفید باشد. انتخاب رنگ با HSB نسبت به RGB ملموس تر است.
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB در مختصات قطبی
HSB در ابتدا برای مختصاب قطبی(بر اساس زاویه و شعاع) به جای مختصات دکارتی طراحی شده است. برای ترسیم عملکرد HSB در مختصات قطبی، نیاز به فاصله و زاویه از مرکز بیلبورد تا مختصات پیکسل مورد نظر داریم. برای این منظور نیاز به استفاده از تابع
length() و تابع atan(y,x) داریم. هنگام استفاده از بردار ها مثل vect2, 3, 4 حتی اگر نمایانگر رنگ باشند، باز هم به عنوان بردار با آنان رفتار میشود. ما در مورد رنگ ها و بردار ها به طور مشابه رفتار میکنیم، شما خواهید دید این انعطاف پذیری مفهمومی بسیار مفید است.
length وجود دارند مانند: distance(), dot(), cross, normalize(), faceforward(), reflect() و refract(). همچنین GLSL دارای توابع مخصوص برداری هستند مانند:: lessThan(), lessThanEqual(), greaterThan(), greaterThanEqual(), equal() و notEqual().وقتی مقدار طول و زاویه را بدست آوردیم باید آن هارا بین 0.0 و 1.0 نرمالایز کنیم. در خط 27,
atan(y,x) زاویه ای بین PI و _PI (-3.14 تا 3.14) برمیگرداند, پس باید آنان را بر TWO_PI تقسیم کنیم(در بالای کد تعریف شده است) تا مقداری بین 0.5 و -0.5 بدست بیاوریم, که میتوان آن را به راحتی به بازه 0.0 تا 1.0 تبدیل کرد. ماکسیمم مقدار شعاعی که میتوان به دست آورد 0.5 است، چون شعاع را از وسط صفحه محاسبه میکنیم، پس باید این بازه را در 2 ضرب تا مقدار ماکسیمم یک را بتوان از آن گرفت.همانطور که میبینید همه چیز حول انتقال و مپ کردن در بازه 0.0 و 1.0 است، همانطور که ما دوست داریم.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
تمرین های زیر را امتحان کنید:
* مختصات قطبی را تغییر دهید که مثال بالا به دور محورش بچرخد، مانند آیکون انتظار موس.
* برای گسترش مقدار یک رنگ خاص و فشرده کردن بقیه، از یک تابع شکل دهی به همرا تبدیل HSB به RGB استفاده کنید.
* اگر با دقت به چرخ رنگی که در برنامه های انتخاب رنگ استفاده میشود نگاه کنید، آنان از فضای رنگی دیگری یعنی، RYB استفاده میکنند، برای مثال رنگ روبروی قرمز در تصویر زیر سبز است، اما در مثال بالا روبروی قرمز فیروزه ای است. آیا میتواید با استفاده از توابع شکل دهی، مثال بالا را مانند شکل زیر کنید؟
* کتاب Josef Albers' book Interaction of Color را بخوانید ، همچین شیدر های زیر را برای تمرین مرور کنید.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### نکته در مورد توابع و آرگومان ها
قبل از رفتن به فصل بعدی، بایستی به عقب برگردیم، و توابع مثال های قبل را ببینیم. متوجه in قبل از آرگومان های ورودی تابع شدید؟. آن یک qualifierاست و در این حالت مشخص میکند، متغیر فقط خواندنی است. در مثال های آینده میبینیم که آرگومان ها را به عنوان
out و یا inout هم میتوان مشخص کرد. آخری یعنی, inout مشابه انتقال آرگومان از طریق رفرنس است، که امکان تغییر یک متغیر عبور داده شده را میدهد.glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
شاید باورتان نشود اما هم اکنون تمام عناصر لازم جهت کشیدن تصاویر باحال را دارید. در فصل بعدی خواهیم آموخت چگونه با ترکیب فضا، همه ترفند های خود را برای ساخت فرم های هندسی ترکیب کنیم
---
06/README Fr (06/README-fr.md)
Couleurs
Jusqu'ici, nous avons manipulé des vecteurs mais nous n'avons pas encore pris le temps de voir comment marchent ces variables.
Avant d'aller plus loin, il est important d'en savoir plus sur ces variables.
Aborder la couleur est un bon moyen de se pencher sur la question.
Si vous connaissez la Programmation Orientée Objet, vous aurez remarqué que nous accédons aux données des vecteurs comme on le ferait avec des
struct en C, grâce à des accesseurs / mutateurs (getters / setters en anglais).Bien que cette pratique soit méconnue, il est possible d'utiliser des
struct en GLSL, plus d'informations ici.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Dans l'exemple ci dessus, x, y et z permettent d'accéder aux 3 valeurs contenues dans l'objet
red de type vec3, ce sont les accesseurs aux propriétés de red.Définir une couleur avec x, y et z peut être un peu déroutant, c'est pourquoi il existe différentes manières d'accéder aux valeurs des propriétés des vecteurs.
Les valeurs de
.x, .y et .z peuvent être récupérées avec les accesseurs .r, .g et .b, ou .s, .t et .p. (.s, .t et .p sont généralement utilisées pour encoder les coordonnées spatiales des textures, nous verrons ça dans les chapitres suivants).
Il est également possible d'accéder aux valeurs des propriétés des vecteurs par leur position d'index dans l'objet: [0], [1] and [2].Les lignes suivantes montrent les différentes manières d'accéder aux données :
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
On peut les utiliser ces accesseurs de façon indépendante ; le code suivant crée un clone newColor du vecteur color en utilisant chaque fois un accesseur différent.
glslvec4 color = vec4( 1.,0.,0.5,1. );
vec4 newColor = vec4( color[0], color.g, color.z, color.q );
Il est possible de combiner les propriétés en concaténant les accesseurs : si on veut exploiter les valeurs
.r, .g et .b d'un vecteur 4 sans se soucier de .a (l'alpha), on peut écrire :glslvec4 color = vec4( 1.,0.,0.5,1. );
vec4 newColor = vec4( color.rgb, 1.0 );
Ce qui revient à cloner chaque proriété
.r, .g et .b du vecteur color sauf la dernière .a.Dans ce cas,
color.rgb est interprété comme un vecteur de type vec3 et il contient les valeurs .r, .g et .b du vec4 color.
De même, si on écrit:glslvec4 color = vec4( 1.,0.,0.5,1. );
vec3 newColor = vec3( color.xy, 1.0 );
On va utiliser les valeurs
.x et .y de color pour construire un vec3 dont les valeurs .r et .g seront les mêmes que les valeurs .r et .g du vecteur color et où la valeur .b sera 1.0.Dernière chose, l'ordre dans lequel on concatène les accesseurs est important.
Si on veut construire un vecteur à partir d'un autre mais en inversant l'ordre des propriétés, on peut l'écrire comme suit :
glslvec3 color = vec3( 1.0, 0.0, 0.5 );
vec3 newColor = color.bgr;
le vecteur newColor va copier les propriétés de color mais au lieu de les copier dans l'ordre "normal":
.r, .g et .b,
il va les copier dans l'ordre inverse: .b, .g et .r.glslcolor.r = 1.0
color.g = 0.0
color.b = 0.5
// et
newColor.r = 0.5
newColor.g = 0.0
newColor.b = 1.0
Il en découle que si les déclarations suivantes sont équivalentes :
glslcolor.rgba = color.xyzw = color.stpq
Ces déclarations ne le sont pas :
glslcolor.rgba != color.argb != color.rbga != color.abgr // etc.
color.xyzw != color.wxyz != color.xzyw != color.wzyx // etc.
color.stpq != color.qstp != color.sptq != color.qpts // etc.
C'est une fonctionnalité très puissante ; elle permet de stocker les informations dans un format compact.
Un exemple d'utilisation, si on veut décrire un rectangle, on peut se servir soit de 2
vec2 décrivant respectivement le coin supérieur gauche et le coin inférieur droit,
ou bien, utiliser un seul vec4 dont l'accesseur .xy renverra un vec2 décrivant le coin supérieur gauche, et l'accesseur .zw renverra un vec2 décrivant le coin inférieur droit.Ces différentes manière d'accéder aux variables à l'intérieur des vecteurs sont simplement là pour nous aider à écrire un code lisible.
Cette souplesse d'utilisation est le point d'entrée qui vous permettra de penser aux espaces cartésiens (le "vrai" espace) et colorimétriques de façon interchangeable.
La concaténation prend tout son sens lorsqu'on veut pouvoir combiner des vecteurs dans un ordre arbitraire pour les mélanger (cette propriété s'appelle le swizzle).
glslvec3 jaune, magenta, vert;// crée le jaune
jaune.rg = vec2(1.0); // Assigne 1. au canaux rouges et vert du vecteur jaune
jaune[2] = 0.0; // Assigne 0. au canal bleu du vecteur jaune
// crée le magenta
magenta = jaune.rbg; // Assigne the valeur en intervertissant le vert et le bleu ( rbg au lieu de rgb )
// crée le vert
vert.rgb = jaune.bgb; // Assigne le canal bleu du jaune (0) aux canaux rouges et bleus
Mélanger les couleurs
A présent que nous savons définir les couleurs, il est temps de les utiliser avec ce que nous avons déjà appris.
mix(), qui permet de mélanger deux valeurs en fonction d'un pourcentage.Comme vu au chapitre précédent,
mix() permet d'interpoler entre 2 valeurs grâce à une troisième valeur T.
Exactement comme smoothstep() à la différence que cette fois, la fonction mix() prend en argument des vecteurs au lieu de floats.Pouvez vous deviner ce que devra être le pourcentage ?
Evidemment, une valeur normalisée entre 0.0 et 1.0 !
Ce qui nous va très bien, après tout ce temps passé sur la palissade du chapitre précédent, il est enfin temps de frapper !
Observez la ligne 18: notez que nous utilisons la valeur absolue (
abs()) d'une fonction de sinus (sin()) prenant le temps en argument pour contrôler le mélange entre colorA et colorB.<div class="codeAndCanvas" data="mix.frag"></div>
Montrez de quoi vous êtes capable :
* Créez une transition expressive entre les couleurs. Pensez à une émotion en particulier.
* Quelle couleur représente le mieux cette émotion ? Comment apparaît-elle ? Comment disparaît-elle ?
* Pensez à une autre émotion et à la couleur correspondante, changez les couleur A et B dans le code puis utilisez les fonctions de formes pour opérer la transition.
Robert Penner a développé une série de fonctions destinées à créer des animations, connues sous le nom d'easing functions.
Vous pouvez utiliser cet exemple comme base de recherche mais les meilleures transitions seront celles que vous ferez vous mêmes.
Jouer avec les dégradés
mix() peut faire plus.
Au lieu de passer un seul float pour faire l'interpolation, nous pouvons passer successivement plusieurs valeurs de transition.
Dans l'exemple suivant, nous passons les valeurs r, g et b d'un vec3 (pct pour pourcentage) pour contrôler le mélange des canaux.Regardez l'exemple suivant.
Comme au chapitre précédent, nous branchons la valeur de transition sur la valeur normalisée de x et nous la visualisons par une ligne.
Au début, rien d'exceptionnel, le dégradé est linéaire sur les trois canaux.
A présent, décommentez la ligne 25 et regardez ce qui se passe, puis décommentez les lignes 26 et 27.
Rappelez vous que les lignes représentent la quantité de mélange entre
colorA et colorB par canal de couleur.<div class="codeAndCanvas" data="gradient.frag"></div>
Vous aurez sans doute reconnu les 3 fonctions de forme du chapitre précédent aux lignes 25, 26, 27.
A vous de jouer ! Il est temps d'explorer et de créer des dégradés intéressants en ré-utilisant ce que nous avons vu au chapitre précédent.
Essayez les exercices suivants :
* Composez un dégradé qui ressemble au coucher de soleil ci-dessus.
* Animez une transition entre lever et coucher de soleil en utilisant
u_time.* Pouvez vous créer un arc-en-ciel avec ce que vous avez appris jusqu'à présent ?
* Utilisez la fonction
step() pour créer un drapeau.HSB
On ne peut pas parler de couleur sans parler d'espace colorimétrique. Vous le savez sans doute, il existe plusieurs façons d'organiser les canaux rouge, vert, bleu.
HSB signifie "Hue, Saturation, Brightness (ou Value)" soit en bon français "Teinte, Saturation, Luminosité", c'est une manière plus intuitive d'organiser les couleurs.
Prenez un instant pour lire et essayer de comprendre les méthodes
rgb2hsv() et hsv2rgb() dans le code suivant.En indexant la Teinte (Hue) sur la position en x et la Luminosité (Brigthness) sur la position en y, nous obtenons un spectre des couleurs complet.
Cette distribution spatiale des couleurs peut être très pratique ; il est plus simple de choisir une couleur dans un espace HSB que dans un espace RGB.
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB et coordonnées polaires
A l'origine, HSB a été conçu pour être représenté dans un système de coordonnées polaires.
Par opposition à un système de coordonnées cartésien décrit par 2 axes X et Y orthogonaux, un système de coordonnées polaires, est décrit par des angles et des rayons.
Pour dessiner notre fonction HSB, nous devons obtenir la position du centre du canvas de manière à connaître l'angle et la distance de chaque fragment au centre.
Pour cela nous allons utiliser la méthode
length() et atan(y,x) qui est l'équivalent GLSL de la méthode atan2(y,x).Lorsqu'on utilise des vecteurs avec des fonctions trigonométriques les variables de type
vec2, vec3 et vec4 sont considérées comme des vecteurs géométriques même si elles représentent des couleurs.
Nous commencerons à traiter les couleurs et les vecteurs géométriques de façon similaire, en fait, vous devriez comprendre assez vite que cette flexibilité d'utilisation est une force.Note : Si vous vous demandez s'il existe d'autres fonctions géométriques que
length
comme : distance(), dot(), cross, normalize(), faceforward(), reflect() et refract(), la réponse est oui.
GLSL expose également des méthodes pour comparer les vecteurs entres eux : lessThan(), lessThanEqual(), greaterThan(), greaterThanEqual(), equal() et notEqual().Une fois que nous avons récupéré l'angle entre le centre et le fragment en cours, nous devons le normaliser.
Ligne 27,
atan(y,x) nous retourne un angle en radians compris entre -PI et PI (~-3.14 to ~3.14), pour le normaliser, nous devons diviser cet angle par 2 x PI, la macro TWO_PI en début de code nous permet de stocker cette valeur.
En divisant l'angle par TWO_PI, nous obtenons un chiffre compris entre -0.5 (-PI / TWO_PI) et 0.5 (PI / TWO_PI) auquel il nous suffit d'ajouter 0.5 pour qu'il soit compris entre 0.0 et 1.0.Le rayon (la distance du centre au fragment) retournera une valeur max de 0.5 en x et en y (parce que nous calculons la distance depuis le centre du canvas),
nous devons donc doubler cette valeur (en la multipliant par 2) pour obtenir un rayon compris entre 0.0 et 1.0.
Vous voyez que tout est question de ramener les valeurs dans l'espace entre 0.0 et 1.0, un espace normalisé.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Essayez les exercices suivants :
* Modifiez l'exemple des coordonnées polaires pour faire tourner les couleurs.
* Utilisez les fonctions de formes avec les fonctions de conversion HSB vers RGB pour étendre une valeur de teinte spécifique et rétrécir le reste.
* Si vous regardez attentivement la roue des couleurs qu'utilisent les sélecteurs de couleurs (voir l'image ci-dessous), ils affichent un spectre différent basé sur du RYB.
Par exemple, la couleur opposée au rouge devrait être le vert mais dans nos exemples, c'est le Cyan.
Pouvez vous trouver un moyen d'arranger ça de manière à obtenir exactement le meme rendu que sur l'image ? Indice: c'est un bon moment pour vous servir des fonctions de formes.
* Lisez le livre de Josef Albers : L'Interaction des Couleurs et servez vous des exemples suivants pour vous entraîner à reproduire ses exemples.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160507154604,160507160421" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### A propos des fonctions et des arguments
Avant de passer au chapitre suivant, attardons nous un peu sur les fonctions du dernier exemple.
Vous remarquerez un
in avant les types des arguments.
C'est ce qu'on appelle un qualifier et dans ce cas précis, cela signifie que la variable est en lecture seule.
Dans les exemples suivants, nous verrons qu'il est également possible de donner les qualifiers out et inout aux variables passées aux fonctions.
Cette dernière, inout, est équivalente à passer un argument par référence en C ; cela nous donne la possibilité de modifier la valeur de la variable passée en argument.glslint newFunction(in vec4 aVec4, // lecture seule
out vec3 aVec3, // écriture seule
inout int aInt); // lecture / écriture
Vous ne le savez pas encore et vous pourriez ne pas le croire mais nous avons à présent tout ce qu'il nous faut pour dessiner à peu près n'importe quoi.
Au prochain chapitre, nous verrons comment combiner ces techniques pour mélanger l'espace. Oui... mélanger l'espace !
---
06/README Id (06/README-id.md)
Warna
Kami tidak memiliki banyak kesempatan untuk membicarakan jenis vektor GLSL. Sebelum melangkah lebih jauh, penting untuk mempelajari lebih lanjut tentang variabel-variabel ini dan subjek warna adalah cara yang bagus untuk mengetahuinya lebih lanjut.
Jika Anda terbiasa dengan paradigma pemrograman berorientasi objek, Anda mungkin memperhatikan bahwa kami telah mengakses data di dalam vektor seperti
struct biasa mirip-C.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Mendefinisikan warna menggunakan notasi x, y dan z bisa membingungkan dan menyesatkan, bukan? Itulah mengapa ada cara lain untuk mengakses informasi yang sama ini, tetapi dengan nama yang berbeda. Nilai dari
.x, .y dan .z juga bisa disebut .r, .g dan .b, dan .s, .t dan .p . (.s, .t dan .p biasanya digunakan untuk koordinat spasial suatu tekstur, yang akan kita lihat di bab selanjutnya.) Anda juga dapat mengakses data dalam vektor dengan menggunakan posisi indeks , [0], [1] dan [2].Baris berikut menunjukkan semua cara untuk mengakses data yang sama:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Cara berbeda untuk menunjuk ke variabel di dalam vektor hanyalah tata nama yang dirancang untuk membantu Anda menulis kode yang jelas. Fleksibilitas yang tertanam dalam bahasa shading ini merupakan pintu bagi Anda untuk mulai berpikir secara bergantian tentang warna dan koordinat ruang.
Fitur hebat lainnya dari jenis vektor di GLSL adalah bahwa properti dapat digabungkan dalam urutan apa pun yang Anda inginkan, yang memudahkan untuk mentransmisikan dan mencampur nilai. Kemampuan ini disebut swizzle.
glslvec3 yellow, magenta, green;// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
Mencampur warna
Sekarang setelah Anda mengetahui bagaimana warna didefinisikan, sekarang saatnya untuk mengintegrasikannya dengan pengetahuan kita sebelumnya. Di GLSL ada fungsi yang sangat berguna,
mix(), yang memungkinkan Anda mencampur dua nilai dalam persentase. Bisakah Anda menebak berapa kisaran persentasenya? Ya, nilai antara 0,0 dan 1,0! Yang sempurna untuk Anda, setelah berjam-jam berlatih gerakan karate Anda dengan pagar - sekarang saatnya menggunakannya!Periksa kode berikut pada baris 18 dan lihat bagaimana kita menggunakan nilai absolut gelombang sin dari waktu ke waktu untuk mencampur
colorA dan colorB.<div class="codeAndCanvas" data="mix.frag"> </div>
Pamerkan keahlian Anda dengan:
* Buat transisi ekspresif antar warna. Pikirkan emosi tertentu. Warna apa yang paling mewakili itu? Bagaimana tampilannya? Bagaimana cara memudar? Pikirkan emosi lain dan warna yang cocok untuknya. Ubah warna awal dan akhir kode di atas agar sesuai dengan emosi tersebut. Kemudian animasikan transisi menggunakan fungsi pembentuk. Robert Penner mengembangkan serangkaian fungsi pembentukan yang populer untuk animasi komputer yang dikenal sebagai fungsi pelonggaran, Anda dapat menggunakan contoh ini sebagai penelitian dan inspirasi tetapi hasil terbaik akan datang dari membuat transisi Anda sendiri.
Bermain dengan gradien
mix () memiliki lebih banyak hal untuk ditawarkan. Alih-alih satu float, kita bisa meneruskan tipe variabel yang cocok dengan dua argumen pertama, dalam kasus kita vec3. Dengan melakukan itu kita mendapatkan kendali atas persentase pencampuran dari setiap saluran warna individual, r, g dan b.Lihat contoh berikut. Seperti contoh di bab sebelumnya, kami mengaitkan transisi ke koordinat x yang dinormalisasi dan memvisualisasikannya dengan garis. Saat ini semua saluran berada di jalur yang sama.
Sekarang, hapus komentar pada baris nomor 25 dan lihat apa yang terjadi. Kemudian coba hapus komentar pada baris 26 dan 27. Ingatlah bahwa baris memvisualisasikan jumlah
colorA dan colorB yang akan digabungkan per saluran.<div class = "codeAndCanvas" data = "gradient.frag"> </div>
Anda mungkin mengenali tiga fungsi pembentuk yang kami gunakan pada baris 25 hingga 27. Mainkanlah dengan mereka! Saatnya bagi Anda untuk menjelajahi dan memamerkan keahlian Anda dari bab sebelumnya dan membuat gradien yang menarik. Coba latihan berikut:
* Buat gradien yang menyerupai matahari terbenam William Turner
* Menganimasikan transisi antara matahari terbit dan terbenam menggunakan
u_time.* Bisakah Anda membuat pelangi menggunakan apa yang telah kita pelajari sejauh ini?
* Gunakan fungsi
step() untuk membuat bendera warna-warni.HSB
Kita tidak dapat berbicara tentang warna tanpa berbicara tentang ruang warna. Seperti yang mungkin Anda ketahui, ada beberapa cara berbeda untuk mengatur warna selain dengan saluran merah, hijau dan biru.
HSB adalah singkatan dari Hue, Saturation and Brightness (atau Value) dan merupakan organisasi warna yang lebih intuitif dan berguna. Luangkan waktu sejenak untuk membaca fungsi
rgb2hsv() dan hsv2rgb() pada kode berikut.Dengan memetakan posisi pada sumbu x ke Hue dan posisi pada sumbu y ke Brightness, kita mendapatkan spektrum warna yang terlihat bagus. Distribusi warna spasial ini bisa sangat berguna; lebih intuitif untuk memilih warna dengan HSB dibandingkan dengan RGB.
<div class = "codeAndCanvas" data = "hsb.frag"> </div>
HSB dalam koordinat kutub
HSB awalnya dirancang untuk direpresentasikan dalam koordinat kutub (berdasarkan sudut dan jari-jari), bukan koordinat kartesius (berdasarkan x dan y). Untuk memetakan fungsi HSB kita ke koordinat kutub, kita perlu mendapatkan sudut dan jarak dari pusat billboard ke koordinat piksel. Untuk itu kita akan menggunakan fungsi
length() dan atan(y, x) (yaitu versi GLSL dari atan2(y, x)) yang umum digunakan.Saat menggunakan fungsi vektor dan trigonometri,
vec2, vec3, dan vec4 dianggap sebagai vektor meskipun keduanya mewakili warna. Kami akan mulai memperlakukan warna dan vektor dengan cara yang sama, bahkan Anda akan menemukan fleksibilitas konseptual ini sangat memberdayakan.Catatan: Jika Anda bertanya-tanya, ada lebih banyak fungsi geometris selain
panjang seperti: distance(), dot(), cross, normalize(), faceforward(), reflect() and refract(). Also GLSL has special vector relational functions such as: lessThan(), lessThanEqual(), greaterThan(), greaterThanEqual(), equal() dan notEqual().Setelah kita mendapatkan sudut dan panjang, kita perlu "menormalkan" nilainya ke kisaran antara 0,0 hingga 1,0. Pada baris 27,
atan(y, x) akan mengembalikan sudut dalam radian antara -PI dan PI (-3.14 hingga 3.14), jadi kita perlu membagi angka ini oleh TWO_PI (didefinisikan di bagian atas kode) untuk mendapatkan nilai antara -0,5 hingga 0,5, yang dengan penambahan sederhana kita ubah ke kisaran yang diinginkan dari 0,0 hingga 1,0. Jari-jari akan mengembalikan maksimum 0,5 (karena kita menghitung jarak dari pusat viewport) jadi kita perlu menggandakan jarak ini (dengan mengalikan dua) untuk mendapatkan maksimum 1,0.Seperti yang Anda lihat, permainan kami di sini adalah tentang mengubah dan memetakan rentang ke 0,0 hingga 1,0 yang kami sukai.
<div class = "codeAndCanvas" data = "hsb-colorwheel.frag"> </div>
Coba latihan berikut:
* Ubah contoh kutub untuk mendapatkan roda warna yang berputar, seperti ikon mouse menunggu.
* Gunakan fungsi pembentukan bersama dengan fungsi konversi dari HSB ke RGB untuk memperluas nilai rona tertentu dan mengecilkan sisanya.
* Jika Anda melihat lebih dekat pada roda warna yang digunakan pada pemilih warna (lihat gambar di bawah), mereka menggunakan spektrum yang berbeda sesuai dengan ruang warna RYB. Misalnya, warna kebalikan dari merah harus hijau, tetapi dalam contoh kita itu adalah cyan. Dapatkah Anda menemukan cara untuk memperbaikinya agar terlihat persis seperti gambar berikut? [Petunjuk: ini adalah momen yang tepat untuk menggunakan fungsi pembentukan.]
* Baca buku Josef Albers 'Interaction of Color dan gunakan contoh shader berikut sebagai latihan.
<div class = "glslGallery" data = "160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties = "clickRun: editor, openFrameIcon: false, showAuthor: false"> </div>
#### Catatan tentang fungsi dan argumen
Sebelum melompat ke bab berikutnya, mari kita berhenti dan mundur. Kembali dan lihat fungsi di contoh sebelumnya. Anda akan melihat
in sebelum jenis argumen. Ini adalah qualifier dan dalam hal ini menetapkan bahwa variabel hanya-baca. Dalam contoh mendatang kita akan melihat bahwa dimungkinkan juga untuk mendefinisikan argumen sebagai out atau inout. Yang terakhir ini, inout, secara konseptual mirip dengan meneruskan argumen dengan referensi yang akan diberikan kami kemungkinan untuk memodifikasi variabel yang dilewatkan.glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
Anda mungkin tidak percaya, tetapi sekarang kami memiliki semua elemen untuk membuat gambar yang keren. Pada bab selanjutnya kita akan belajar bagaimana menggabungkan semua trik kita untuk membuat bentuk geometris dengan memadukan ruang. Ya ... memadukan ruang.
---
06/README It (06/README-it.md)
Colori
Non abbiamo ancora avuto occasione di parlare dei tipi di vettore in GLSL. Prima di andare avanti, è importante imparare meglio come funzionano queste variabili e parlare di colori è un buon modo per capirle meglio.
Se siete pratici con i paradigmi di programmazione orientata agli oggetti, probabilmente avrete notato che stiamo accedendo ai dati interni ai vettori, come se fossero una qualunque
`struct` di C.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Definire il colore usando una notazione x, y e z può essere fuorviante e creare confusione, vero? Per questo esistono altri modi di accedere alle stesse informazioni, con nomi diversi. I valori di
`.x`, `.y` e `.z` possono anche essere chiamati `.r`, `.g` e `.b`, e `.s`, `.t` e `.p`. (`.s`, `.t` e `.p` solitamente sono usati per le coordinate spaziali di una texture, che vedremo nel prossimo capitolo). Puoi anche accedere ai valori in un vettore usando gli indici di posizione `[0]`, `[1]` e `[2]`.Le righe seguenti mostrano tutti i modi per accedere agli stessi valori:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Questi diversi modi di indicare le variabili all’interno di un vettore sono solo nomenclature progettate per aiutarvi nella scrittura di un codice chiaro. Tale flessibilità incorporata nel linguaggio shading è una porta per iniziare a pensare all’intercambiabilità tra colore e coordinate spaziali.
Un’altra grandiosa caratteristica dei tipi di vettore in GLSL è che le proprietà possono essere combinate in qualsiasi ordine voi vogliate, e ciò rende più facile manipolare e mescolare i valori. Quest’abilità è chiamata swizzle.
glslvec3 yellow, magenta, green;// Per creare il giallo
yellow.rg = vec2(1.0); // Assegnare 1. ai canali del rosso e del verde
yellow[2] = 0.0; // Assegnare 0. al canale del blu
// Per fare il magenta
magenta = yellow.rbg; // Invertite il canale del verde con quello del blu
// Per ottenere il verde
green.rgb = yellow.bgb; // Assegnare il canale blu del giallo (0) ai canali rosso e blu
Mescolare i colori
Ora che sapete come sono definiti i colori, è tempo di integrare ciò con le nostre conoscenze precedenti. In GLSL c’è una funzione molto utile,
`mix()`, che vi permette di mescolare due valori in percentuali. Riuscite ad indovinare l’estensione della percentuale? Esatto, i valori tra 0.0 e 1.0! Dopo lunghe ore di esercizio, finalmente è ora di mettere le vostre conoscenze in pratica!Guardate il codice seguente alla riga 18 e osservate come stiamo usando i valori assoluti di un’onda sinusoidale nel tempo per mescolare
`colorA` e `colorB`.<div class="codeAndCanvas" data="mix.frag"></div>
Mettete in risalto le vostre capacità:
* Create una transizione espressiva tra colori. Pensate a un’emozione particolare. Quale colore la rappresenta meglio? Come appare? Come si dissolve? Pensate ad un’altra emozione e al colore corrispondente. Cambiate il colore iniziale e finale del codice soprastante per ricreare quelle emozioni. Poi animate la transizione usando le funzioni di forma. Robert Penner ha sviluppato una serie di funzioni di forma famose per animazioni al computer, note come easing functions. Potete usare quest’esempio come ricerca ed ispirazione ma il risultato migliore sarà ottenuto facendo le vostre transizioni.
Giocare con i gradienti
`mix()` ha molto da offrire. Invece di usare un solo `float`, possiamo passare un tipo di variabile che abbina i primi due argomenti, nel nostro caso un `vec3`. Facendo così, possiamo controllare le percentuali di ciascun canale colore `r`, `g` e `b`.Guardate l'esempio seguente. Così come negli esempi nel capitolo precedente, stiamo collegando la transizione alla x normalizzata e la visualizziamo con una linea. In questo momento tutti e tre i canali seguono la medesima linea.
Ora, togliete il commento alla riga 25 e guardate cosa succede. Poi provate a togliere il commento alle righe 26 e 27. Ricordate che le linee visualizzano la quantità di
`colorA` e `colorB` da mescolare per ogni canale.<div class="codeAndCanvas" data="gradient.frag"></div>
Probabilmente riconoscerete le tre funzioni di forma che stiamo usando dalla riga 25 alla 27. Giocate con queste! È il momento per esplorare e mostrare le vostre abilità acquisite nel capitolo precedente e creare gradienti interessanti. Provate i seguenti esercizi:
* Create un gradiente che assomigli al tramonto di William Turner.
* Animate una transizione tra un'alba e un tramonto usando
`u_time`.* Riuscite a creare un arcobaleno usando ciò che abbiamo imparato fino ad ora?
* Usate la funzione
`step()` per creare una bandiera variopinta.HSB
Non possiamo parlare di colore senza menzionare lo spazio di colore. Come probabilmente saprete, ci sono diversi modi di organizzare il colore, oltre ai canali del rosso, verde e blu.
HSB sta per Hue (Tonalità), Saturation (Saturazione) e Brightness (Luminosità o Valore) ed è un modo di organizzare i colori più intuitivo e utile. Prendete un momento per leggere le funzioni
`rgb2hsv()` e `hsv2rgb()` nel codice seguente.Mappando la posizione sull'asse x con la tonalità e la posizione sull'asse y con la luminosità, otteniamo uno spettro dei colori visibili. Questa distribuzione spaziale del colore può essere molto pratica; è più intuitivo selezionare un colore con HSB che con RGB.
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB nelle coordinate polari
Originariamente HSB è stato creato per essere rappresentato in coordinate polari (basate su angoli e raggio) e non in coordinate cartesiane (basate su x e y). Per unire la nostra funzione HSB alle coordinate polari, dobbiamo ottenere l'angolo e la distanza dal centro del canvas per ogni coordinata pixel. Per far questo usiamo la funzione
`length()` e `atan(y,x)` (che è la versione GLSL della più comune `atan2(y,x)`).Quando si usano vettori e funzioni trigonometriche,
`vec2`, `vec3` e `vec4` sono considerati come vettori anche quando rappresentano i colori. Inizieremo a considerare in modo simile i colori e i vettori, e in realtà troverete che questa flessibilità concettuale è molto potente.Nota: se ve lo stavate chiedendo, esistono altre funzioni geometriche oltre a
`length`, come: `distance()`, `dot()`, `cross`, `normalize()`, `faceforward()`, `reflect()` e `refract()`. Anche GLSL ha speciali funzioni vettoriali come: `lessThan()`, `lessThanEqual()`, `greaterThan()`, `greaterThanEqual()`, `equal()` e `notEqual()`.Una volta che otteniamo l'angolo e la lunghezza dobbiamo "normalizzare" i loro valori in una scala tra 0.0 e 1.0. alla riga 27,
`atan(y,x)` restituirà un angolo in radianti tra –PI e PI (-3.14 e 3.14), quindi dobbiamo dividere questo numero per `TWO_PI` (definito nella parte superiore del codice) per ottenere valori tra -0.5 e 0.5, che con una semplice addizione cambiamo nella scala desiderata tra 0.0 e 1.0. Il raggio restituirà un massimo di 0.5 (perché stiamo calcolando la distanza dal centro del punto di osservazione), quindi abbiamo bisogno di raddoppiare questa distanza (moltiplicando per due) per ottenere un massimo di 1.0.Come potete vedere, il nostro gioco qui riguarda semplicemente il trasformare e manipolare le distanze tra 0.0 e 1.0.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Provate gli esercizi successivi:
* Modificate l'esempio polare per ottenere una ruota di colori che gira, proprio come l'icona del mouse che indica il caricamento.
* Usate una funzione di forma unita alla funzione di conversione da HSB a RGB per espandere un particolare valore di tonalità e comprimere il resto.
* Se guardate da vicino la ruota cromatica usata dai selettori del colore (guardate l'immagine qui sotto), essi usano uno spettro diverso da quello dello spazio di colore RYB. Per esempio, il colore opposto al rosso dovrebbe essere il verde, ma nel nostro esempio è il ciano. Riuscite a trovare un modo per rimediare e far sì che sembri esattamente l'immagine seguente? [Suggerimento: è un buon momento per usare le funzioni di forma].
* Leggete il libro di Josef Albers "L'interazione dei colori” e usate i seguenti esempi di shader come pratica.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Nota sulle funzioni e sugli argomenti.
Prima di addentrarci nel capitolo successivo, fermiamoci e torniamo indietro. Osservate le funzioni negli esempi precedenti. Noterete un
`in` prima di definire il tipo degli argomenti. Questo è un qualificatore e in questo caso specifica che la variabile è solamente letta. Negli esempi futuri vedremo che è anche possibile definire argomenti come `out` o `inout`. Quest'ultimo, `inout`, è concettualmente simile a passare un argomento per referenza, il che ci dà la possibilità di modificare una variabile passata.glslint newFunction(in vec4 aVec4, // solo lettura
out vec3 aVec3, // solo scritture
inout int aInt); // lettura e scrittura
Potreste non crederci ma ora abbiamo tutti gli elementi per creare dei fantastici disegni. Nel prossimo capitolo impareremo come si possono combinare tutte queste tecniche per creare forme geometriche fondendo lo spazio. Sì, avete capito bene, fondendo lo spazio.
---
06/README Jp (06/README-jp.md)
色について
GLSLのベクトル型について、まだあまり話す機会がありませんでした。先に進む前にこれらの変数についてもっと学んでおくことが大事です。ここで色について考えることはベクトル型をより良く理解するためのとても良い題材になります。
オブジェクト指向のプログラミングに慣れている方であれば、ここまでベクトルの中のデータに、C言語の
`struct` やその種の構造体と同様の方法でアクセスしてきたことに気づいたでしょう。glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
色をx, y, zで表して定義するのは分かりにくいし誤解を招きそうですね。そのため、ベクトル型の変数では同じ情報に違う名前を使ってアクセスできるようになっています。
`.x` `.y` `.z` の代わりに `.r` `.g` `.b` もしくは `.s` `.t` `.p` を使うことができるのです(`.s` `.t` `.p` は通常、テクスチャ空間の座標を示すのに使われます。これについては後の章で学びます)。ベクトル型のデータにはまた、 `[0]` `[1]` `[2]` といったインデックスを使ってアクセスすることもできます。下記のコードでは同じデータにアクセスするための全て方法を示しています。
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
ベクトルの中の値を呼び表すためのこれらの方法は、分かりやすいコードを書くために用意された仕組みです。シェーダー言語に備わったこの柔軟性は、色と空間座標を交換可能なものとして考える足がかりになります。
もう1つ、GLSLのベクトル型の素晴らしい機能として、これらの値の名前を好きな順番で組み合わせて使えることが挙げられます。このおかげで簡単に値を入れ替えたり、異なる型へ変換することができます。この機能はswizzleと呼ばれています。
glslvec3 yellow, magenta, green;// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
色の調合
`mix()` 関数があり、2つの値をパーセンテージを指定して混ぜ合わせることができます。
パーセンテージの範囲はわかりますか? そう、0.0 から 1.0 ですね。長い時間をかけてミヤギさんの壁でカラテの修行を積んだあなたにはピッタリです。さあ学んだ技を披露しましょう。下記のコードの18行目で、サイン波の値の絶対値が
`colorA` と `colorB` を混ぜ合わせるために使われている様子を見てください。<div class="codeAndCanvas" data="mix.frag"></div>
さあ、あなたの技を披露する番です。
* 色を表情豊かに変化させてください。なにか特定の感情を思い浮かべてみましょう。一番ふさわしいのは何色ですか? どのように現れてどのように消えていくのでしょう? もう1つ別の感情について考えて、ふさわしい色を選んでください。上記のサンプルの始まりと終わりの色をこの2つの感情の色に変えてください。次にシェイピング関数を使って色を滑らかに変化させましょう。
Robert Pennerはイージング関数と呼ばれる、コンピューターアニメーションでよく使われる一連の関数を開発しました。このサンプルを使って研究したりインスピレーションを得ることもできますが、一番良いのはあなた自身で関数を作ってみることでしょう。
グラデーションで遊ぶ
`mix()` 関数には幅広い使い道があります。3つ目の引数として単に `float` で割合を指定する代わりに、最初の2つの引数に対応する型の値を渡すことができます。上記のサンプルでは `vec3` を使いました。こうすることで `r`, `g`, `b` のそれぞれのチャンネルを個別のパーセンテージで混ぜ合わせることができます。下記のサンプルを見てください。前章のサンプルと同じように、色の変化を正規化されたxの値に対応させて線で示しています。今のところ全ての色のチャンネルはこの同じ線に対応しています。
25行目のコメントを外してどうなるか見てみましょう。26行目と27行目のコメントも外してみてください。
<div class="codeAndCanvas" data="gradient.frag"></div>
おそらく、25〜27行目で3つの異なるシェイピング関数が使われていることに気づいたでしょう。これらの関数を書き換えて遊んでみましょう。色々と実験をして面白いグラデーションを作り、前章で学んだ技を披露するチャンスです。下記を試してみましょう。
* ウィリアム・ターナーの夕日のようなグラデーションを作り出してみましょう。
*
`u_time` を使って日の出から日の入りまでの変化のアニメーションを作ってみましょう。* 今までに学んだことを使って虹を作り出すことはできますか?
*
`step()` 関数を使ってカラフルな旗を作ってください。HSB
色空間を抜きにして色のことを語ることはできません。おそらくご存知の通り、赤、緑、青のチャンネルで表す以外にも色を体系化する方法があります。
HSBは色相(Hue)、彩度(Saturation)、明度(Brightness または Value)の頭文字を取ったもので、より直感的で便利な色の体系です。
`rgb2hsv()` と `hsv2rgb()` の2つの関数をよく読んでみてください。x軸を色相、y軸を明度に割り当てると色のスペクトルを作ることができます。HSBの空間的な色の配置は非常に便利で、HSBを使うと、RGBよりも直感的に色を選ぶことができます。
<div class="codeAndCanvas" data="hsb.frag"></div>
HSBと極座標
HSBはもともとデカルト座標(xとy)ではなく、極座標(中心からの角度と距離)で色を示す仕組みです。私たちのHSB関数を極座標に対応させるには、ピクセル座標を元に、描画領域の中心からの角度と距離を求めなくてはなりません。そのためには
`length()` 関数と `atan(y,x)` 関数を使います。 `atan(y,x)` は一般的な言語で使われている `atan2(y,x)` のGLSL版です。ベクトルや三角関数を扱う際には
`vec2`, `vec3` や `vec4` を、それらが色を表している場合でもベクトルと見なします。私たちは色とベクトルを同等に扱います。そして、このフレキシブルな考え方が様々なことを実現する支えになるのを目の当たりにするでしょう。`length` の他にも沢山の幾何学関数があります。`distance()`
- `dot()`
- `cross`
- `normalize()`
- `faceforward()`
- `reflect()`
- `refract()`また、GLSLにはベクトル型の値を比較するための特別な関数があります。
`lessThan()`
- `lessThanEqual()`
- `greaterThan()`
- `greaterThanEqual()`
- `equal()`
- `notEqual()`.`atan(y,x)` は角度を -π から π(-3.14...から3.14...)の間の値で返すので、これをコードの冒頭で定義されている `TWO_PI` で割って -0.5 から 0.5 の値にします。そして単純に 0.5 を足せば望み通りの 0.0 から 1.0 の値が手に入ります。描画領域の中心から距離を測っているので、半径は最大で 0.5 になります。最大値を 1.0 にするためにはこの幅を倍にする(2を掛ける)必要があります。このように、値の範囲を変換して 0.0 から 1.0 の扱いやすい値にすることが大事です。
(訳注:この半径についての説明は正確ではありません。例えば描画領域の中心から角までの距離を2倍にするとルート2になるので1.0を超えてしまいます。必ず値が0.0から1.0に収まるようにするにはどうすればいいか、また
`hsb2rgb` 関数に想定外の大きな値や小さな値を渡すとどうなるか考えて試してみましょう。)<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
下記の課題に挑戦してみましょう。
* 上の極座標のサンプルをMacOSのレインボーカーソルのような、回転する色の輪に変えてください。
* シェイピング関数と、HSBをRGBへ変換する関数とを組み合わせて、特定の色相を広げてその他の色相が狭くなるようにしてください。
* 下の画像を見てください。カラーピッカーで使われている色の輪をよく見ると、サンプルコードとは違うRYB色空間に基づく色のスペクトルが使われていることに気づくでしょう(訳注:これはアプリケーション次第です。必ずこうなっているとは限りません)。例えば赤の反対は緑のはずですが、サンプルコードではシアンになっています。これを修正して下の画像とまったく同じに見えるようにする方法を考えられるでしょうか(ヒント:シェイピング関数を使うチャンスです)。
#### 関数と引数についての注釈
次の章に進む前にちょっと立ち止まって振り返ってみましょう。引数の型の前にある
`in` に気づいたでしょう。これは修飾子(qualifier) と呼ばれるもので、この場合は引数が読み取り専用であることを示しています。この後の章では引数を `out` (書き出し専用) や `inout` (読み書き両方可能) に指定する例も出てきます。最後の `inout` は参照を引数として渡すのと概念的に同じで、渡された変数を変更するようにしてくれます。(訳注:関数の中で変数の値を変更すると、関数に引数として渡した元々の変数の値にも反映されます。)glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
今はまだ信じられないかもしれませんが、もう私たちはクールな絵を描くために必要な全てを手に入れました。次の章では全ての技を組み合わせて幾何学的な図形を描く方法を学びます。
---
06/README Pl (06/README-pl.md)
Kolory
Nie mieliśmy zbytnio okazji porozmawiać o typach wektorowych w GLSL.
Zanim przejdziemy dalej, ważne jest żebyśmy dowiedzieli się o nich więcej, a temat kolorów może być w tym bardzo pomocny.
Jeżeli paradgymat programowania obiektowego jest ci bliski, to prawdopodobnie zauważyłeś, że proces ekstrakcji danych z wektorów wygląda podobnie jak ekstracja danych z
struct'ów w C.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Definiowanie kolor za pomocą notacji x, y i z jest trochę mylące, prawda? Właśnie dlatego istnieją inne sposoby dostępu do tej samej informacji, ale za pomocą innych nazw. Wartości
.x, .y i .z mogą być również uzyskane za pomocą .r, .g i .b, jak i .s, .t i .p (.s, .t i .p są zwykle używane przy współrzędnych tekstur, które zobaczymy w następnych rozdziałach). Możesz również uzyskać dane wektora za pomocą indeksów [0], [1] i [2].Następujący kod przedstawia wszystkkie sposoby uzysakania tych samych danych:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Ta mnogość metod uzyskiwania tych samych danych jest tylko po to, aby ułatwić pisanie zrozumiałego kodu. Ta elastyczność wbudowana w język shadingowy stanowi też okazję, żebyś zaczął myśleć o współrzędnych koloru i przestrzeni jako wzjamenie zastępowalnych.
Inną świetną własnością typów wektorowych w GLSL jest to, że współrzędne mogą być mieszane w dowolnej kolejności, co ułatwia castowanie i mieszanie wartości. Właśność ta nazywana jest swizzle'owaniem.
glslvec3 yellow, magenta, green;// Tworzenie żółtego
yellow.rg = vec2(1.0); // Przypisanie 1. do kanału czerwonego i zielonego
yellow[2] = 0.0; // Przypisanie 0. do kanału niebieskiego
// Tworzenie magenty
magenta = yellow.rbg; // Przypisanie wektora z przestawionym kanałem zielonym i niebieskim
// Tworzenie zielonego
green.rgb = yellow.bgb; // Przypisanie kanału niebieskiego do kanału czerwonego i niebieskiego
Mieszanie koloru
Teraz, gdy już wiesz jak definiuje się kolory, pora na połączenie tego z naszą wcześniejszą wiedzą. W GLSL istnieje bardzo przydatna funkcja
mix(), która pozwala mieszać dwie wartości wobec określonego stosunku wyrażonego w procentach. Potrafisz zgadnąć jaki jest zakres procentów? Oczywiście, że 0.0 i 1.0! Czas na wykorzystanie naszego shadingowego karate!Sprawdź linijkę 18 poniższego kodu i zoabcz jak używamy wartości bezwględnej z sinusa od czasu, aby mieszać
colorA i colorB.<div class="codeAndCanvas" data="mix.frag"></div>
Pokaż na co cie stać:
* Stwórz ekspresywną tranzycje między kolorami. Pomyśl o konkretnej emocji. Jaki kolor najlepiej ją reprezentuje? Jak wygląda? Jak zanika? Pomyśl o innej emocji i pasującym do niej kolorze. Zmień
colorA i colorB w kodzie powyżej, aby pasowały do tych emocji. Następnie zanimuj tę tranzycję za pomocą shaping function. Robert Penner stworzył serię popularnych shaping functions, z zasotoswaniami w animacji komputerowej, zwanych easing functions, skorzystaj z tego przykładu jako inspiracji, jednak najlepsze rezultaty osiągniesz tworząc własne tranzycje.Zabawa z gradientem
mix() ma więcej do zaoferowania. Zamiast pojedynczego floata, możemy podać zmienną tego samego typu, co dwa pierwsze argumenty; w naszym wypadku jest to vec3. W ten sposób zdobywamy kontrolę nad stosunkiem mieszania każdego indywidualnego kanału koloru, r, g i b.Spójrz na poniższy przykład. Tak jak w przykładach z poprzedniego rozdziału, dzięki znormalizowanej współrzędnej
x tworzymy gradient i wizualizujemy go za pomocą linii. Aktualnie, wszystkie kanały leżą na tej samej linii.
Odkomentuj linjkę 25 i zobacz, co się stanie. Następnie odkomentuj linijki 26 i 27. Pamiętaj, że linie wizualizują, w jakim stosunku kanały (R, G, B) kolorów
colorA i colorB są obecne w ostatecznym gradiencie. <div class="codeAndCanvas" data="gradient.frag"></div>
Prawdopodobnie rozpoznajesz trzy shaping functions, które używamy w linijkach 25 i 27. Baw się nimi! Czas pokazać swoje umiejętności z poprzednich rozdziałów, tworząc interesujące gradienty. Spróbuj następujących ćwiczeń:
* Skomponuj gradient przypominający zachód słońca Williama Turnera.
* Zanimuj tranzycje między wschodem i zachodem za pomocą
u_time.* Czy potrafisz stworzyć tęczę korzystając z tego, czego nauczyliśmy się dotychaczas?
* Użyj
step(), by stworzyć kolorową flagę.HSB
Nie da się mówić o kolorze bez poruszenia tematu przestrzeni barw. Jak prawdopodobnie wiesz, istnieją też inne sposoby reprezentacji koloru poza RGB (z kanałem czerwonym, zielonym i niebieskim).
HSB oznacza Hue (pol. "barwa"), Saturation (pol. "nasycenie") i Brightness (pol. "jasność) i jest o wiele bardziej intuicyjną reprezentacją koloru niż RGB. Czasem Brightness nazywany jest "Value", stąd zamiast HSB można spotkać się też ze skrótem HSV. Przyjrzyj się funkcjom
rgb2hsv() i hsv2rgb() w następującym kodzie:Mapując pozycję na osi x do barwy i pozycję na osi y do jasności, otrzymujemy spektrum widzialnych kolorów. O wiele bardziej intuicyjnie wybiera się kolor HSB niż RGB.
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB we współrzędnych biegunowych
Oryginalnie, HSB miało być reprezentowane z pomocą współrzędnych biegunowych (opartych na kącie i promieniu), a nie kartezjańskich (opartych na x i y). By zmapować naszą funkcję HSB do współrzędnych biegunowych, musimy otrzymać kąt i dystans od centrum kanwy do współrzędnej piksela. W tym celu użyjemy funkcje
length() i atan(y,x) (który jest odpowiednikiem atan2(y,x) w GLSL).Pamiętaj:
vec2, vec3 i vec4 traktowane są jak wektory nawet jeśli reprezentują kolor. Zaczniemy traktować kolory i wektory bardzo podobnie. length jak: distance(), dot(), cross, normalize(), faceforward(), reflect() i refract(). Ponadto, GLSL ma specjalne funkcje do porównywania wektorów: lessThan(), lessThanEqual(), greaterThan(), greaterThanEqual(), equal() i notEqual().Gdy już zdobędziemy kąt i promień, musimy znormalizować ich wartości do zakresu od 0.0 do 1.0. W linijce 27,
atan(y,x) zwróci kąt w radianach między -PI a PI (-3.14 a 3.14), więc musimy podzielić tę liczbę przez TWO_PI (zdefiniowane na górze kodu), uzyskując wartości między -0.5 i 0.5, które, przez proste dodawanie, mapujemy dalej do zakresu od 0.0 do 1.0. Promień ma długość 0.5 (ponieważ liczymy odległość od środka kanwy), więc musimy podwoić ten zakres (mnożąc przez 2), by uzyskać 1.0.Jak widzisz, sedno leży w transformowaniu i mapowaniu zakresów do 0.0 i 1.0.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Spróuj poniższych ćwiczeń:
* Zmodyfikuj przykład ze współrzędnymi biegunowymi, aby uzyskać kręcące się (jak w ikonce czekania myszki) koło barw.
* Użyj shaping function razem z funkcją konwersji z HSB do RGB, aby rozszerzyć jedną barwę i zwężyć inną.
* Jeśli przyjrzysz się uważnie kołom barw używanym w narzędziach do wybierania koloru (ang "color picker") (spójrz na obrazek poniżej), zobaczysz, że używają innego spektrum koloru, zgodnego z przestrzenią barw RYB. Przykładowo, w RGB kolorem przeciwnym do czerwonego jest cyjan, a w RYB - zielony. Czy potrafiłbyś zrekonstruować poniższy obrazek? [Wskazówka: to dobry moment, by użyć shaping functions]
* Przeczytaj książkę Josefa Albersa _Interaction of Color_ i przestudiuj poniższe przykłady shaderów.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Uwaga o funkcjach i ich argumentach
Zanim przeskoczysz do następnego rozdziału, zatrzymajmy się na chwilę. Wróć do funkcji
hsb2rgb z poprzedniego interaktywnego przykładu. Zauważysz in przed typem argumentu. Jest to kwalifikator (ang. "qualifier") i akurat ten oznacza, że zmienna jest tylko do odczytu. W przyszłości zobaczymy, że jest również możliwe, by poprzedzić argumenty kwalifikatorami out i inout. Kwalifikator out określa, że argument jest tylko do zapisu (ang. "write-only"), natomiast inout działa podobnie jak przekazywanie argumentu przez referencje, co umożliwia również modyfikowanie go. glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
Może w to nie uwierzysz, ale mamy wszystkie składniki potrzbne do tworzenia fajnych rysunków. W następnym rozdziale nauczymy się jak połączyć wszystkie poznane tricki, by stworzyć geometryczne formy/kształty przez blendowanie przestrzeni. Dobrze słyszysz... blendowanie przestrzeni.
---
06/README Pt (06/README-pt.md)
Cores
Ainda não tivemos a chance de conversar sobre os tipos de vetores em GLSL. Mas antes de seguirmos adiante, é importante aprender mais sobre estas variáveis e o tópico de cores é uma ótima forma de os entendermos melhor.
Se você está familiarizado com os paradigmas de programação orientada a objetos, você provavelmente tenha percebido que estamos acessando dados dentro de vetores como qualquer
struct em C.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Definir cores usando a notação x, y e z pode ser confuso, certo? Por esta razão, existem outras formas de acessar esta mesma informação mas com nomes diferentes. Os valores de
.x, .y e .z podem também ser chamados de .r, .g e .b, e também, .s, .t e .p. (.s, .t e .p. normalmente são usados para coordenadas espaciais de uma textura, na qual veremos mais tarde em outro capítulo). Você pode também acessar os dados de um vetor usando o índice de posição: [0], [1] e [2].As próximas linhas mostram todas as formas de acessar os mesmos dados:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Essas formas diferentes de apontar para variáveis dentro de vetores são apenas nomenclaturas criadas para ajudar você a criar um código mais limpo. Esta flexibilidade embutida em linguagem de shader é a porta para começar a pensar alternadamente em cores e coordenadas espaciais.
Outra ótima característica dos tipos vetoriais em GLSL é que as propriedades podem ser combinadas em qualquer ordem, o que facilita a manipulação dos valores. Esta habilidade é chamada de swizzle.
glslvec3 yellow, magenta, green;// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
Misturando cores
Agora que você sabe como cores são definidas, chegou a hora de integrar isso com nosso conhecimento prévio. Em GLSL, existe uma função muito útil chamada
mix(), que permite que você misture dois valores em porcentagens. Você consegue adivinhar em qual intervalo esta porcentagem é? Sim, estes valores vão de 0.0 a 1.0! O que é perfeito para você, depois destas longas horas praticando seus movimentos de karatê na cerca - É hora de usá-los!Dê uma olhada na linha 18 do código a seguir e veja se nós conseguimos usar valores absolutos de uma onda seno pelo tempo para misturar
colorA e colorB.<div class="codeAndCanvas" data="mix.frag"></div>
Mostre suas habilidades ao:
* Fazer uma transição expressiva entre as cores. Pense em uma emoção em particular, qual cor parece representá-la melhor? Como ela aparece? Como ela se esvai? Pense em outra emoção e determine uma cor para ela. Mude as cores do início de do final no código acima para corresponder aquelas emoções. Então, anime a transição usando funções. Robert Penner desenvolveu uma série sobre funções populares para animação computacional conhecido como funções de suavização. você pode usar este exemplo como pesquisa e inspiração mas os melhores resultados virão das suas próprias funções.
Experimentando com degradês
mix() tem mais a oferecer. Ao invés de passar um único float, nós podemos passar uma variável do tipo que correspondente aos dois primeiros argumentos, neste caso, uma vec3. Fazendo isso, nós ganhamos controle sobre a porcentagem de mesclagem para cada canal de cor individualmente, r, g e b.Dê uma olhada no exemplo a seguir. Como os exemplos no capítulo anterior, nós estamos encaixando a transição na coordenada x normalizada e a visualizando com uma linha. Agora todos os canais seguem esta mesma linha.
Agora, descomente a linha de número 25 e veja o que acontece. Então tente descomentar as linhas 26 e 27. Lembre-se que as linhas representam a intensidade de
colorA e colorB a serem misturar por canal.<div class="codeAndCanvas" data="gradient.frag"></div>
Você provavelmente reconheceu os três modelos de funções que estamos usando nas linhas 25 a 27. Explore com eles! Agora é hora para experimentar e exibir suas habilidades do capítulo anterior e criar gradientes interessantes. Tente os seguintes exercícios:
* Componha um gradiente que se assemelha com entardecer de William Turner
* Anime uma transição entre o nascer do sol e o pôr do sol usando
u_time.* Você consegue criar um arco-íris usando o que foi aprendido até agora?
* Use a função
step() para criar uma bandeira colorida.HSB
Nós não podemos falar sobre cores sem mencionar o espaço de cores. Como você provavelmente sabe, existem diferentes formas de organizar cores além de canais de vermelho, verde e azul.
HSB vem de Hue(Matiz), Saturation(Saturação) e Brightness (Brilho ou Valor) e é uma forma muito mais intuitiva e útil de organizar cores. Pare um momento para ler as funções
rgb2hsv() e hsv2rgb() no código a seguir.Mapeando a posição no eixo x para a matiz e a posição no eixo y para brilho, nós obtemos um lindo espectro de cores visíveis. Esta distribuição espacial de cores pode ser muito útil; é muito mais intuitivo para escolher uma cor com HSB do que com RGB
<div class="codeAndCanvas" data="hsb.frag"></div>
HSB em coordenadas populares
HSB foi originalmente criada para ser representada em coordenadas polares (baseada em ângulo e raio) ao invés de coordenadas cartesianas (baseadas em x e y). Para mapear nossa função de HSB em coordenadas polares, precisamos obter o ângulo e a distância do centro da tela para a coordenada do píxel. Para isso, usaremos a função
length() e atan(y,x) (que é uma versão GLSL da comumente usada atan2(y,x)). Ao utilizar vetores e funções trigonométricas,
vec2, vec3 e vec4 são tratados como vetores mesmo quando elas representam cores. Nós começaremos a tratar cores e vetores de forma similar e você verá que esta flexibilidade conceitual é muito poderosa.Nota: Se você estiver se perguntando, existem mais funções geométricas além de
length, como distance(), dot(), cross, normalize(), faceforward(), reflect() e refract(). Também em GLSL tem funções relacionais especiais para vetorais como: lessThan(), lessThanEqual(), greaterThan(), greaterThanEqual(), equal() e notEqual().Uma vez que obtemos o ângulo e a tamanho, precisamos "normalizar" seus valores para o intervalo entre 0.0 e 1.0. Na linha 27,
atan(y,x) retornará um ângulo em radianos entre -PI e PI (-3.14 a 3.14), então precisamos dividir este número por TWO_PI (definido no topo do código) para obtermos valores entre -0.5 até 0.5, que, com uma simples adição, nós mudamos para o intervalo desejado de 0.0 a 1.0. O raio retornará um máximo de 0.5 (porque nós estamos calculando a distância a partir do centro da tela), então nós precisamos dobrar este intervalo (multiplicando por dois) para obter um máximo de 1.0.Como você pode observar, nosso jogo aqui é sobre transformar e mapear intervalos para 0.0 a 1.0 que gostamos.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Tente os seguintes exercícios:
* Modifique o exemplo de coordenadas polares para obter uma roda de cores que gira, assim como o ícone de espera do mouse.
* Use a modelagem de função junto com a função de conversão de HSB para RGB para expandir um valor da matiz em particular e comprimir o resto.
* Se você olhar de perto na roda de cores usada em seletores de cor (veja a imagem abaixo), elas usam um diferente espectro de acordo com o espaço de cores em RYB. Por exemplo, a cor oposta ao vermelho deve ser verde, mas no nosso exemplo é ciano. Você consegue encontrar um jeito de consertar isso para parecer exatamente com a imagem a baixo? [Dica: este é um grande momento para usar modelagem de funções.]
* Leia Josef Albers' book Interaction of Color (EM INGLÊS) e use os seguintes exemplos de shader como prática.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Nota sobre funções e argumentos
Antes de irmos para o próximo capítulo, vamos parar e rebobinar. Volte e dê uma olhada nas funções nos exemplos anteriores. Você vai perceber um
in antes dos tipos de argumentos. Isso é um qualificador e neste caso, especifica que a variável é somente leitura. Em exemplos futuros, veremos que também é possível definir argumentos como out ou inout. Este último, inout, é conceitualmente similar a passar um argumento pela referência na qual nos dará a possibilidade de modificar uma variável passada.glslint newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
Você pode não acreditar, mas agora nós temos todos os elementos para fazer desenhos legais. No próximo capítulo, aprenderemos como combinar todos os truques para criar formas geométricas mesclando o espaço. Isso mesmo... mesclando espaço.
---
06/README Ru (06/README-ru.md)
Цвета
Ранее мы не очень подробно раскрывали тему работы с векторами в GLSL. Прежде чем двинуться дальше, не помешает изучить векторы поглубже, и работа с цветом как нельзя лучше подходит для этого.
Если вы знакомы с объектно-ориентированной парадигмой программирования, вы скорее всего заметили, что мы обращаемся к данным в векторах на манер структур (
struct) в языке С.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Работа с цветом через x, y и z может сбить с толку и ввести в заблуждение, не так ли? Поэтому в GLSL можно получить доступ к тем же самым данным через другие имена. Значения
.x, .y и .z так же именуются .r, .g и .b, и .s, .t и .p. Последний набор имён обычно используется для пространственных координат текстур, с которыми мы познакомимся в следующих главах. Кроме того, для доступа к элементам вектора можно использовать численные индексы [0], [1] и [2].Следующие строки демонстрируют все способы доступа к одним и тем же данным:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Все эти способы - всего лишь дополнительные имена для доступа к одним и тем же данным, которые призваны помочь вам в создании более понятного кода. Эта гибкость языка шейдеров станет путеводной нитью, помогающей вам мыслить о значениях цвета и координатах пространства как о взаимозаменяемых сущностях.
Другое замечательное свойство векторных типов GLSL - это возможность доступа к координатам в произвольном порядке, которая упрощает преобразование и смешивание значений.
glslvec3 yellow, magenta, green;// Задаём жёлтый цвет
yellow.rg = vec2(1.0); // Записываем 1.0 в красный и зелёный каналы
yellow[2] = 0.0; // Записываем 0.0 в синий
// Задаём малиновый цвет
magenta = yellow.rbg; // Меняем местами зелёный и синий
// Задаём зелёный цвет
green.rgb = yellow.bgb; // Записываем значение синего канала жёлтого цвета (0) в красный и синий
Смешивание цветов
Теперь, когда вы знаете как задаются цвета, мы можем собрать все новые знания воедино. В GLSL есть очень полезная функция
mix(), которая смешивает значения в указанной пропорции. Угадайте, как задаются пропорции? Конечно же, числом от 0.0 до 1.0! И это просто отлично, учитывая те долгие часы, что вы практиковали движения карате с забором. Самое время применить их на практике!В строке 18 в следующем куске кода мы используем абсолютное значение синуса от времени для смешивания цветов
A и B.<div class="codeAndCanvas" data="mix.frag"></div>
Покажите, что вы можете:
* Создайте экспрессивный переход между цветами. Вообразите какую-нибудь эмоцию. Какой цвет больше всего ассоциируется с ней? Как она появляется? Как она сходит на нет? Придумайте другую эмоцию и подходящий для неё цвет. Поменяйте начальный и конечный цвета в коде выше в соответствии с этими эмоциями. Анимируйте переход с помощью функций формы. Роберт Пеннер разработал набор популярных функций для компьютерной анимации, известный под названием упрощающих функций. Вы можете использовать этот пример для исследования и поиска вдохновения, но вы сможете достигнуть наилучшего результата только создав собственные функции перехода.
Играемся с градиентами
mix() способна на большее. Вместо одного числа с плавающей точкой мы можем передать переменную того же типа, что и первые два аргумента (vec3 в нашем случае). Таким образом мы можем управлять пропорциями каждого канала r, g и b по отдельности.Рассмотрим следующий пример. Как и в примерах предыдущей главы, мы преобразуем значение перехода к нормализованной координате x и визуализируем его с помощью линии. Сейчас все каналы изменяются по одному и тому же закону.
Теперь раскомментируйте строку 25 и посмотрите что произойдёт. Затем попробуйте раскомментировать строки 26 и 27. Помните, что линии показывают пропорции каждого канала при смешивании цветов
A и B.<div class="codeAndCanvas" data="gradient.frag"></div>
Вы скорее всего узнали три функции, которые мы используем в строках 25-27. Поиграйте с ними! Исследуйте и демонстрируйте ваши находки, используя умения из предыдущей главы для создания интересных градиентов. Попробуйте выполнить следующие упражнения:
* Создайте градиент, повторяющий закат Вильяма Тёрнера.
* Сделайте анимацию перехода от рассвета к закату с помощью
u_time.* Можете ли вы сделать радугу, используя изученный материал?
* Используйте функцию
step() для создания цветного флага.HSB
Нельзя рассказать о цветах, не упомянув цветовое пространство. Как вы возможно знаете, есть множество способов задания цвета кроме красного, зелёного и синего каналов.
HSB) расшифровывается как оттенок (Hue), насыщенность (Saturation) и яркость (Brightness или Value), и является более интуитивным способом представления цвета. Прочитайте код функций
rgb2hsv() и hsv2rgb() в примере ниже.Отображая координату
x в оттенок, а координату y - в яркость, мы получаем красивый спектр видимых цветов. Такое пространственное распределение цветов очень удобно. Выбор цветов в пространстве HSB более интуитивен, чем RGB.<div class="codeAndCanvas" data="hsb.frag"></div>
HSB в полярных координатах
Пространство HSB изначально было разработано для представления в полярных координатах (на основе угла и радиуса), вместо декартовых (x и y). Чтобы перевести нашу функцию HSB в полярные координаты, нужно вычислить угол и расстояние, используя центр области рисования и декартовы координаты пикселя. Для этого мы используем функцию вычисления расстояния
length() и двухаргументный арктангенс (atan(y,x), GLSL-версия известной функции atan2(y,x)).Тригонометрические функции от векторного аргумента вычисляются покомпонетно, даже если вы используете векторы для представления цветов. Мы начинаем использовать цвета и векторы одинаковым способом. Такая концептуальная гибкость является очень мощным инструментом.
На заметку: Возможно, вы спросите, какие ещё есть геометрически функции кроме длины (
length)? Их довольно много: dot() (скалярное произведение), cross (векторное произведение), normalize() (привести вектор к единичной длине), faceforward() (вернуть вектор, указывающий в то же полупространство, что и данный), reflect() (отражение) и refract() (преломление). Так же в GLSL есть векторные функции сравнения: lessThan() (меньше), lessThanEqual() (меньше либо равно), greaterThan() (больше), greaterThanEqual() (больше либо равно), equal() (равно) и notEqual() (не равно).Получив угол и длину, мы должны нормировать их значения в интервал от 0.0 до 1.0. В строке 27
atan(y,x) возвращает угол в радианах от минус пи до пи, поэтому сначала мы разделим его на удвоенное пи (TWO_PI, определено вначале кода), и к полученному числу от -0.5 до 0.5 прибавим 0.5, чтобы перейти в нужный интервал от 0.0 до 1.0. Максимальный радиус будет равен 0.5 (мы вычисляли расстояние от центра окна), поэтому его нужно удвоить.Как видите, в этом разделе мы в основном играли с преобразованием значений в нужный нам промежуток от 0.0 до 1.0.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Попробуйте выполнить следующие упражнения:
* Модифицируйте пример с полярными координатами так, чтобы цветовой круг вращался, как указатель мыши в режиме ожидания.
* Используйте функции формы совместно с функцией преобразования HSB->RGB для расширения области круга с каким-нибудь одним оттенком и урезания других оттенков.
* Присмотревшись к цветовому кругу в программах для подбора цвета (изображён ниже), можно заметить, что он пострен на основе красного, жёлтого и синего цветов. Например, напротив красного должен быть зелёный, но в нашем примере выше там находится голубой. Исправьте пример, так чтобы он выглядел в точности как изображение ниже (подсказка: используйте функции формы).
* Прочитайте книгу «Взаимодействие цветов» Джозефа Альберса и воспользуйтесь следующим примером для практики.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Заметки о функциях и аргументах
Перед тем как нырнуть в следующую главу, давайте остановимся и немного отмотаем назад. Вернитесь и взгляните на функции в предыдущих примерах. Вы заметите слово
in перед типами аргументов. Это - квалификатор, и в данном случае он означает, что переменная предназначена только для чтения. В последующих примерах мы увидим так же аргументы с квалификаторами out и inout. Последний эквивалентен передаче переменной по ссылке, при которой мы можем изменить переданное значение так, что изменения становятся видны за пределами функции.glslint newFunction(in vec4 aVec4, // только для чтения
out vec3 aVec3, // только на запись
inout int aInt); // чтение и запись
Вы не поверите, но мы уже изучили всё необходимое для создания крутой графики. В следующей главе мы научимся комбинировать все эти трюки для создания геометрических фигур с помощью смешивания пространства. Именно, смешивание пространства!
---
06/README Tr (06/README-tr.md)
Renkler
GLSL vektör tipleri hakkında konuşma fırsatımız pek olmadı. Daha ileri gitmeden önce bu değişkenler hakkında daha fazla bilgi edinmek önemlidir ve renkler konusu onlar hakkında daha fazla şey öğrenmenin harika bir yoludur.
Nesne yönelimli programlama paradigmalarına aşinaysanız, muhtemelen vektörlerin içindeki verilere herhangi bir normal C-benzeri
struct gibi eriştiğimizi fark etmişsinizdir.glslvec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
Rengi x, y ve z gösterimi kullanarak tanımlamak kafa karıştırıcı ve yanıltıcı olabilir, değil mi? Bu yüzden aynı bilgiye farklı adlarla erişmenin başka yolları da vardır.
.x, .y ve .z değerleri aynı zamanda .r, .g ve .b olarak, ve .s, .t ve .p olarak da çağrılabilir. (.s, .t ve .p genellikle bir dokunun uzaysal koordinatları için kullanılır, bunu ilerideki bir bölümde göreceğiz.) Vektördeki verilere dizin konumunu kullanarak da erişebilirsiniz: [0], [1] ve [2].Aşağıdaki satırlar aynı veriye erişmenin tüm yollarını gösterir:
glslvec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
Bir vektörün içindeki değişkenlere işaret etmenin bu farklı yolları, açık kod yazmanıza yardımcı olmak için tasarlanmış adlandırma kurallarıdır. Gölgelendirme diline gömülü bu esneklik, renk ve uzay koordinatları hakkında birbirinin yerine düşünmeye başlamanız için bir kapıdır.
GLSL'deki vektör tiplerinin bir diğer harika özelliği, özelliklerin istediğiniz sırada birleştirilebilmesidir; bu da değerleri dönüştürmeyi ve karıştırmayı kolaylaştırır. Bu yeteneğe swizzle denir.
glslvec3 yellow, magenta, green;// Sarı yapma
yellow.rg = vec2(1.0); // Kırmızı ve yeşil kanallara 1 atama
yellow[2] = 0.0; // Mavi kanala 0 atama
// Magenta yapma
magenta = yellow.rbg; // Yeşil ve mavi kanalları değiştirilmiş olarak ata
// Yeşil yapma
green.rgb = yellow.bgb; // Sarının mavi kanalını (0) kırmızı ve mavi kanallara ata
Renk karıştırma
Artık renklerin nasıl tanımlandığını bildiğinize göre, bunu önceki bilgilerimizle entegre etmenin zamanı geldi. GLSL'de çok kullanışlı bir fonksiyon olan
mix() vardır, iki değeri yüzdelerle karıştırmanızı sağlar. Yüzde aralığının ne olduğunu tahmin edebilir misiniz? Evet, 0.0 ile 1.0 arasındaki değerler! Çitle karate hareketleri pratiği yaparak geçirdiğiniz uzun saatlerden sonra bunları kullanmanın zamanı geldi!Aşağıdaki kodu 18. satırda kontrol edin ve
colorA ile colorB'yi karıştırmak için zamana bağlı bir sinüs dalgasının mutlak değerlerini nasıl kullandığımızı görün.<div class="codeAndCanvas" data="mix.frag"></div>
Yeteneklerinizi sergileyerek:
* Renkler arasında ifade gücü yüksek bir geçiş yapın. Belirli bir duygu düşünün. Hangi renk onu en çok temsil ediyor? Nasıl ortaya çıkıyor? Nasıl kayboluyor? Başka bir duygu ve ona uygun renk düşünün. Yukarıdaki kodun başlangıç ve bitiş renklerini bu duygulara uyacak şekilde değiştirin. Ardından şekillendirme fonksiyonlarını kullanarak geçişi canlandırın. Robert Penner, bilgisayar animasyonu için easing fonksiyonları olarak bilinen bir dizi popüler şekillendirme fonksiyonu geliştirdi, araştırma ve ilham olarak bu örneği kullanabilirsiniz ama en iyi sonuç kendi geçişlerinizi yapmaktan gelecektir.
Gradyanlarla oynama
mix() fonksiyonunun sunabileceği daha fazlası var. Tek bir float yerine, ilk iki argümanla eşleşen bir değişken tipi geçirebiliriz, bizim durumumuzda bir vec3. Bunu yaparak her bir renk kanalı olan r, g ve b'nin karışım yüzdeleri üzerinde kontrol kazanırız.Aşağıdaki örneğe bir göz atın. Önceki bölümdeki örnekler gibi, geçişi normalize edilmiş x koordinatına bağlıyoruz ve bir çizgiyle görselleştiriyoruz. Şu anda tüm kanallar aynı çizgi boyunca gidiyor.
Şimdi 25 numaralı satırın yorumunu kaldırın ve ne olduğunu izleyin. Ardından 26 ve 27. satırların yorumunu kaldırmayı deneyin. Satırların, kanal başına karıştırılacak
colorA ve colorB miktarını görselleştirdiğini unutmayın.<div class="codeAndCanvas" data="gradient.frag"></div>
Muhtemelen 25-27. satırlarda kullandığımız üç şekillendirme fonksiyonunu tanıyorsunuzdur. Onlarla oynayın! Önceki bölümdeki becerilerinizi keşfetme ve sergilemenin ve ilginç gradyanlar yapmanın zamanı. Aşağıdaki alıştırmaları deneyin:
* William Turner gün batımına benzeyen bir gradyan oluşturun
*
u_time kullanarak gündoğumu ve gün batımı arasında bir geçiş canlandırın.* Şimdiye kadar öğrendiklerimizi kullanarak bir gökkuşağı yapabilir misiniz?
* Renkli bir bayrak oluşturmak için
step() fonksiyonunu kullanın.HSB
Renk uzayı hakkında konuşmadan renkler hakkında konuşamayız. Muhtemelen bildiğiniz gibi, renkleri kırmızı, yeşil ve mavi kanalların yanı sıra düzenlemenin farklı yolları vardır.
HSB, Ton (Hue), Doygunluk (Saturation) ve Parlaklık (Brightness veya Value) anlamına gelir ve renklerin daha sezgisel ve kullanışlı bir organizasyonudur. Aşağıdaki kodda
rgb2hsv() ve hsv2rgb() fonksiyonlarını okumak için bir dakikanızı ayırın.x eksenindeki konumu Ton'a ve y eksenindeki konumu Parlaklığa eşleyerek, görünür renklerin güzel bir spektrumunu elde ederiz. Bu uzaysal renk dağılımı çok kullanışlı olabilir; HSB ile renk seçmek RGB'den daha sezgiseldir.
<div class="codeAndCanvas" data="hsb.frag"></div>
Kutupsal koordinatlarda HSB
HSB başlangıçta kartezyen koordinatlar (x ve y'ye dayalı) yerine kutupsal koordinatlarda (açı ve yarıçapa dayalı) temsil edilmek üzere tasarlanmıştır. HSB fonksiyonumuzu kutupsal koordinatlara eşlemek için, tuvalin merkezinden piksel koordinatına olan açıyı ve mesafeyi elde etmemiz gerekir. Bunun için
length() fonksiyonunu ve atan(y,x) fonksiyonunu (yaygın olarak kullanılan atan2(y,x)'in GLSL versiyonu olan) kullanacağız.Vektör ve trigonometrik fonksiyonlar kullanırken,
vec2, vec3 ve vec4 renkleri temsil etseler bile vektör olarak ele alınır. Renkleri ve vektörleri benzer şekilde ele almaya başlayacağız, aslında bu kavramsal esnekliğin çok güçlendirici olduğunu göreceksiniz.length'in yanı sıra daha fazla geometrik fonksiyon vardır: distance(), dot(), cross, normalize(), faceforward(), reflect() ve refract(). Ayrıca GLSL'de özel vektör karşılaştırma fonksiyonları vardır: lessThan(), lessThanEqual(), greaterThan(), greaterThanEqual(), equal() ve notEqual().Açıyı ve uzunluğu elde ettikten sonra, değerlerini 0.0 ile 1.0 arasındaki aralığa "normalize" etmemiz gerekir. 27. satırda,
atan(y,x) radyan cinsinden -PI ile PI (-3.14 ile 3.14) arasında bir açı döndürecektir, bu yüzden -0.5 ile 0.5 arasında değerler elde etmek için bu sayıyı TWO_PI (kodun üstünde tanımlanmıştır) ile bölmemiz gerekir, basit bir toplama ile istenen 0.0 ile 1.0 aralığına değiştiririz. Yarıçap maksimum 0.5 döndürecektir (çünkü görüntü alanının merkezinden mesafeyi hesaplıyoruz) bu yüzden maksimum 1.0 elde etmek için bu aralığı ikiye katlamamız (ikiyle çarpmamız) gerekir.Gördüğünüz gibi, buradaki oyunumuz tamamen aralıkları sevdiğimiz 0.0 ile 1.0 aralığına dönüştürmek ve eşlemektir.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Aşağıdaki alıştırmaları deneyin:
* Dönen bir renk çarkı elde etmek için kutupsal örneği değiştirin, tıpkı bekleme fare simgesi gibi.
* Belirli bir ton değerini genişletmek ve geri kalanını daraltmak için HSB'den RGB'ye dönüştürme fonksiyonuyla birlikte bir şekillendirme fonksiyonu kullanın.
* Renk seçicilerde kullanılan renk çarkına yakından bakarsanız (aşağıdaki görüntüye bakın), RYB renk uzayına göre farklı bir spektrum kullanırlar. Örneğin, kırmızının karşıt rengi yeşil olmalıdır, ancak bizim örneğimizde camgöbeğidir. Bunu tam olarak aşağıdaki görüntü gibi görünecek şekilde düzeltmenin bir yolunu bulabilir misiniz? [İpucu: şekillendirme fonksiyonlarını kullanmak için harika bir an.]
* Josef Albers'in Interaction of Color kitabını okuyun ve aşağıdaki shader örneklerini pratik olarak kullanın.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Fonksiyonlar ve argümanlar hakkında not
Bir sonraki bölüme geçmeden önce duralım ve geri saralım. Önceki örneklerdeki fonksiyonlara geri dönün ve bakın. Argüman tiplerinin önünde
in göreceksiniz. Bu bir niteleyici ve bu durumda değişkenin salt okunur olduğunu belirtir. Gelecekteki örneklerde argümanları out veya inout olarak tanımlamanın da mümkün olduğunu göreceğiz. Bu sonuncusu, inout, kavramsal olarak bir argümanı referansla geçirmeye benzer ve bize geçirilen bir değişkeni değiştirme imkanı verir.glslint newFunction(in vec4 aVec4, // salt-okunur
out vec3 aVec3, // salt-yazılır
inout int aInt); // okunur-yazılır
İnanmayabilirsiniz ama artık harika çizimler yapmak için tüm elemanlara sahibiz. Bir sonraki bölümde, uzayı harmanlayarak geometrik formlar yapmak için tüm numaralarımızı nasıl birleştireceğimizi öğreneceğiz. Evet... uzayı harmanlayarak.
#### Araç kutunuz için
* LYGIA'nın renk shader fonksiyonları, GLSL'de renkleri manipüle etmek için yeniden kullanılabilir fonksiyonlar kümesidir. Renk uzayları arasında dönüştürme, renkleri harmanlama, gradyanlar oluşturma ve renk dönüşümleri uygulama fonksiyonları içerir. Yeniden kullanılabilirlik, performans ve esneklik için tasarlanmış çok ayrıntılı bir kütüphanedir. Ve herhangi bir projeye ve framework'e kolayca eklenebilir.
---
06/SUMMARY Pt (06/SUMMARY-pt.md)
Familiarize-se com o modo de se expressar em cores nos shaders. Os exemplos cobrem a maneira de misturar cores e animá-las lindamente ao longo do tempo, bem como a conversão entre dois modelos diferentes (RGB e HSB). Em GLSL, as cores são simplesmente como vetores, o que quer dizer que você pode facilmente aplicar os conceitos e técnicas que aprender aqui para outros.
---
06/SUMMARY (06/SUMMARY.md)
Familiarize yourself with how to express colors in shaders. The examples cover how to mix colors and beautifully animate them over time as well as conversion between two different models(RGB and HSB).
In GLSL, colors are simply just vectors, which means you can easily apply the concepts and techniques you learn here to other
---
07/README (07/README.md)
Shapes
Finally! We have been building skills for this moment! You have learned most of the GLSL foundations, types and functions. You have practiced your shaping equations over and over. Now is the time to put it all together. You are up for this challenge! In this chapter you'll learn how to draw simple shapes in a parallel procedural way.
Rectangle
Imagine we have grid paper like we used in math classes and our homework is to draw a square. The paper size is 10x10 and the square is supposed to be 8x8. What will you do?
You'd paint everything except the first and last rows and the first and last column, right?
How does this relate to shaders? Each little square of our grid paper is a thread (a pixel). Each little square knows its position, like the coordinates of a chess board. In previous chapters we mapped x and y to the red and green color channels, and we learned how to use the narrow two dimensional territory between 0.0 and 1.0. How can we use this to draw a centered square in the middle of our billboard?
Let's start by sketching pseudocode that uses
if statements over the spatial field. The principles to do this are remarkably similar to how we think of the grid paper scenario.glslif ( (X GREATER THAN 1) AND (Y GREATER THAN 1) )
paint white
else
paint black
Now that we have a better idea of how this will work, let’s replace the
if statement with step(), and instead of using 10x10 let’s use normalized values between 0.0 and 1.0:glsluniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Each result will return 1.0 (white) or 0.0 (black).
float left = step(0.1,st.x); // Similar to ( X greater than 0.1 )
float bottom = step(0.1,st.y); // Similar to ( Y greater than 0.1 )
// The multiplication of left*bottom will be similar to the logical AND.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
step() function will turn every pixel below 0.1 to black (vec3(0.0)) and the rest to white (vec3(1.0)) . The multiplication between left and bottom works as a logical AND operation, where both must be 1.0 to return 1.0 . This draws two black lines, one on the bottom and the other on the left side of the canvas.In the previous code we repeat the structure for each axis (left and bottom). We can save some lines of code by passing two values directly to
step() instead of one. That looks like this:glslvec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;
So far, we’ve only drawn two borders (bottom-left) of our rectangle. Let's do the other two (top-right). Check out the following code:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Uncomment lines 21-22 and see how we invert the
st coordinates and repeat the same step() function. That way the vec2(0.0,0.0) will be in the top right corner. This is the digital equivalent of flipping the page and repeating the previous procedure.Take note that in lines 18 and 22 all of the sides are being multiplied together. This is equivalent to writing:
glslvec2 bl = step(vec2(0.1),st); // bottom-left
vec2 tr = step(vec2(0.1),1.0-st); // top-right
color = vec3(bl.x bl.y tr.x * tr.y);
step() and multiplication for logical operations and flipping the coordinates.Before going forward, try the following exercises:
* Change the size and proportions of the rectangle.
smoothstep() instead of step(). Note that by changing values, you can go from blurred edges to elegant smooth borders.floor().* Choose the implementation you like the most and make a function of it that you can reuse in the future. Make your function flexible and efficient.
* Make another function that just draws the outline of a rectangle.
* How do you think you can move and place different rectangles in the same billboard? If you figure out how, show off your skills by making a composition of rectangles and colors that resembles a Piet Mondrian painting.
Circles
It's easy to draw squares on grid paper and rectangles on Cartesian coordinates, but circles require another approach, especially since we need a "per-pixel" algorithm. One solution is to re-map the spatial coordinates so that we can use a
step() function to draw a circle.How? Let's start by going back to math class and the grid paper, where we opened a compass to the radius of a circle, pressed one of the compass points at the center of the circle and then traced the edge of the circle with a simple spin.
Translating this to a shader where each square on the grid paper is a pixel implies asking each pixel (or thread) if it is inside the area of the circle. We do this by computing the distance from the pixel to the center of the circle.
There are several ways to calculate that distance. The easiest one uses the
distance() function, which internally computes the length() of the difference between two points (in our case the pixel coordinate and the center of the canvas). The length() function is nothing but a shortcut of the hypotenuse equation that uses square root (sqrt()) internally.distance(), length() or sqrt() to calculate the distance to the center of the billboard. The following code contains these three functions and the non-surprising fact that each one returns exactly same result.* Comment and uncomment lines to try the different ways to get the same result.
<div class="codeAndCanvas" data="circle-making.frag"></div>
In the previous example we map the distance to the center of the billboard to the color brightness of the pixel. The closer a pixel is to the center, the lower (darker) value it has. Notice that the values don't get too high because from the center (
vec2(0.5, 0.5) ) the maximum distance barely goes over 0.5. Contemplate this map and think:* What you can infer from it?
* How we can use this to draw a circle?
* Modify the above code in order to contain the entire circular gradient inside the canvas.
Distance field
We can also think of the above example as an altitude map, where darker implies taller. The gradient shows us something similar to the pattern made by a cone. Imagine yourself on the top of that cone. The horizontal distance to the edge of the cone is 0.5. This will be constant in all directions. By choosing where to “cut” the cone you will get a bigger or smaller circular surface.
Basically we are using a re-interpretation of the space (based on the distance to the center) to make shapes. This technique is known as a “distance field” and is used in different ways from font outlines to 3D graphics.
Try the following exercises:
step() to turn everything above 0.5 to white and everything below to 0.0.* Inverse the colors of the background and foreground.
smoothstep(), experiment with different values to get nice smooth borders on your circle.* Once you are happy with an implementation, make a function of it that you can reuse in the future.
* Add color to the circle.
* Can you animate your circle to grow and shrink, simulating a beating heart? (You can get some inspiration from the animation in the previous chapter.)
* What about moving this circle? Can you move it and place different circles in a single billboard?
* What happens if you combine distances fields together using different functions and operations?
glslpct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
* Make three compositions using this technique. If they are animated, even better!
#### For your tool box
sqrt() function - and all the functions that depend on it - can be expensive. Here is another way to create a circular distance field by using dot() product.<div class="codeAndCanvas" data="circle.frag"></div>
Useful properties of a Distance Field
Distance fields can be used to draw almost everything. Obviously the more complex a shape is, the more complicated its equation will be, but once you have the formula to make distance fields of a particular shape it is very easy to combine and/or apply effects to it, like smooth edges and multiple outlines. Because of this, distance fields are popular in font rendering, like Mapbox GL Labels, Matt DesLauriers Material Design Fonts and as is described in Chapter 7 of iPhone 3D Programming, O’Reilly.
Take a look at the following code.
<div class="codeAndCanvas" data="rect-df.frag"></div>
We start by moving the coordinate system to the center and shrinking it in half in order to remap the position values between -1 and 1. Also on line 24 we are visualizing the distance field values using a
fract() function making it easy to see the pattern they create. The distance field pattern repeats over and over like rings in a Zen garden.Let’s take a look at the distance field formula on line 19. There we are calculating the distance to the position on
(.3,.3) or vec3(.3) in all four quadrants (that’s what abs() is doing there).If you uncomment line 20, you will note that we are combining the distances to these four points using the
min() to zero. The result produces an interesting new pattern.Now try uncommenting line 21; we are doing the same but using the
max() function. The result is a rectangle with rounded corners. Note how the rings of the distance field get smoother the further away they get from the center.Finish uncommenting lines 27 to 29 one by one to understand the different uses of a distance field pattern.
Polar shapes
In the chapter about color we map the Cartesian coordinates to polar coordinates by calculating the radius and angles of each pixel with the following formula:
glslvec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);
We use part of this formula at the beginning of the chapter to draw a circle. We calculated the distance to the center using
length(). Now that we know about distance fields we can learn another way of drawing shapes using polar coordinates.This technique is a little restrictive but very simple. It consists of changing the radius of a circle depending on the angle to achieve different shapes. How does the modulation work? Yes, using shaping functions!
Below you will find the same functions in the Cartesian graph and in a polar coordinates shader example (between lines 21 and 25). Uncomment the functions one-by-one, paying attention the relationship between one coordinate system and the other.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Try to:
* Animate these shapes.
Combine different shaping functions to cut holes* in the shape to make flowers, snowflakes and gears.
Use the
plot() function we were using in the Shaping Functions Chapter* to draw just the contour.Combining powers
Now that we've learned how to modulate the radius of a circle according to the angle using the
atan() to draw different shapes, we can learn how use atan() with distance fields and apply all the tricks and effects possible with distance fields.The trick will use the number of edges of a polygon to construct the distance field using polar coordinates. Check out the following code from Andrew Baldwin.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Using this example, make a function that inputs the position and number of corners of a desired shape and returns a distance field value.
min() and max().* Choose a geometric logo to replicate using distance fields.
Congratulations! You have made it through the rough part! Take a break and let these concepts settle - drawing simple shapes in Processing is easy but not here. In shader-land drawing shapes is twisted, and it can be exhausting to adapt to this new paradigm of coding.
#### For your toolbox
* LYGIA's draw functions are set of reusable functions to draw 2D shapes and patterns. You can also explore LYGIA's SDF functions folder to combine more complex shapes through distance fields. It's very granular library, designed for reusability, performance and flexibility. And it can be easily be added to any projects and frameworks.
* Down at the end of this chapter you will find a link to PixelSpirit Deck this deck of cards will help you learn new SDF functions, compose them into your designs and use on your shaders. The deck has a progressive learning curve, so taking one card a day and working on it will push and challenge your skills for months.
Now that you know how to draw shapes I'm sure new ideas will pop into your mind. In the following chapter you will learn how to move, rotate and scale shapes. This will allow you to make compositions!
---
07/NOTES (07/NOTES.md)
- [Distance Transforms of Sampled Functions] (http://cs.brown.edu/~pff/papers/dt-final.pdf)
nicolas
* after the pseudocode, explain why not using 'if' instead of step( threshold, value ) as this is (fucking) counter-intuitive :) L 61 'That way the
`vec2(0.0,0.0)`' should rather be : 'That way the `vec2` tl*' to disambiguate it from br which is also a vec2
L 113 the longest distance between a pixel and the center in a normalised space is: SQRT( 2 ) 0.5 = ~0.7071..., not 0.5 (also present ion chapter 6)
* L 123 'a bigger or smaller circular surface' a circular surface is called a 'disc' :)
* L 175 a word about Quadrants and its relation to the bi-unit square maybe? or just explaining why we use a bi-unit square instead of the regular 0-1 normalized space...
* L 217 'how use' missing 'to'---
07/README De (07/README-de.md)
Formen
Endlich! Auf diesen Moment haben wir in den vorangegangenen Kapiteln hingearbeitet! Du hast die wichtigsten Grundlagen von GLSL, seine Datentypen und Funktionen kennengelernt. Und du hast mit formgebenden Funktionen gearbeitet. Jetzt ist es an der Zeit, all dieses Wissen zusammenzuführen. Bist Du bereit? In diesem Kapitel wirst Du lernen, wie man grundlegende geometrische Formen auf parallele, prozedurale Weise erstellt.
Rechtecke
Stell dir vor, Du hast ein Millimeterpapier vor Dir, wie man es zuweilen im Mathematikunterricht nutzt. Und Deine Aufgabe ist es, darauf ein ausgefülltes Quadrat zu zeichnen. Die Größe des Papieres beträgt 10 x 10 und das Quadrat soll 8 x 8 sein. Wie gehst Du vor?
Vielleicht würdest Du einfach die gesamte Fläche ausfüllen und dabei nur die erste und die letzte Zeile auslassen, ebenso wie die erste und die letzte Spalte. So käme das gewünschte Quadrat sehr einfach zustande.
Wie hängt das nun mit Shadern zusammen? Jedes kleine Quadrat auf unserem Millimeterpapier können wir uns als ein Pixel, d.h. einen parallel ausgeführten Thread, vorstellen. Und jedes dieser Quadrate (jeder Thread) kennt seine Koordinate, wie bei einem Schachbrett. In den vorangegangenen Kapiteln haben wir die x- und die y-Ordinate jeweils auf den roten und grünen Farbkanal des Punktes abgebildet. Wir haben gelernt, wie wir den schmalen zweidimensionalen Bereich zwischen
`0.0` und `1.0` bearbeiten. Wie können wir dieses Wissen nun anwenden, um in der Mitte unserer Zeichenfläche ein zentriertes weißes Quadrat zu malen?Lass uns mit etwas Pseudocode beginnen, der mit
`if`-Befehlen auf die Lage des zu bearbeitenden Pixels innerhalb der Zeichenfläche eingeht. Das Vorgehen ähnelt dabei in bemerkenswerte Weise dem oben skizzierten beim Malen auf Millimeterpapier.glslif ( (X GROESSER ALS 1) UND (Y GROESSER ALS 1) )
male weiss
else
male schwarz
Jetzt, wo wir eine Idee haben, wie die Lösung aussehen könnte, lass uns den
`if`-Befehl durch einen Aufruf der `step()`-Funktion ersetzen, und anstelle der Maße 10 x 10 normalisierte Werte zwischen `0.0` und `1.0` bearbeiten:glsluniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// diese Berechnungen liefern jeweils 1.0 (weiss) oder 0.0 (schwarz).
float left = step(0.1,st.x); // entspricht X groesser als 0.1
float bottom = step(0.1,st.y); // entspricht Y groesser als 0.1
// die Multiplikation von left*bottom entspricht der logischen Verknüpfung durch UND
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
`step()`-Funktion setzt jedes Pixel unterhalb von `0.1` auf Schwarz (`vec3(0.0)`) und alle anderen auf Weiß (`vec3(1.0)`). Die Multiplikation von `left` und `bottom` arbeitet wie eine logische UND-Verknüpfung, weil nur dann nicht `0.0`, sondern `1.0` herauskommt, wenn beide Variablen auf `1.0` stehen. So entstehen zwei schwarze Linien, eine am linken und eine am unteren Rand der Zeichenfläche. Der Rest wird weiß.Im obigen Programmcode wiederholen wir die gleiche Vorgehensweise für beide Ränder (links und unten). Wir können das noch etwas kompakter formulieren, indem wir in einem Aufruf zwei Testwerte in Form eines zweidimensionalen Vektors an
`step()` übergeben. Das sieht dann so aus:glslvec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;
Bis jetzt haben wir nur zwei Kanten unseres Rechtecks bearbeitet. Jetzt kommen auch die beiden anderen Kanten – die rechte und die obere – an die Reihe. Schau Dir den folgenden Programmcode an:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Entferne die Kommentarzeichen aus den Zeilen 21-22 und beobachte, wie wir die Koordinaten auf den linken und unteren Rand abbilden (
`1-st`), damit wir sie wieder mit der `step()` Funktion und dem Wert von `0.1` vergleichen können. Aus der oberen rechten Ecke (`vec2(1.0,1.0)`) wird so für unsere Berechnungen quasi die untere linke Ecke `vec2(0.0,0.0)`. Das ist so, als würden wir die Zeichenfläche einfach um 180 Grad drehen und den Test dann wie zuvor wiederholen.Beachte außerdem, dass in den Zeilen 18 und 22 die Ergebnisse von
`step()` in Bezug auf alle vier Seiten miteinander multipliziert werden. Dies entspricht wiederum eine logischen UND-Verknüpfung, denn wir erhalten nur dann Weiß (`1.0`), wenn keines der Ergebnisse Schwarz (`0.0`) ist. Wir könnten auch schreiben:glslvec2 bl = step(vec2(0.1),st); // linke und untere Kante
vec2 tr = step(vec2(0.1),1.0-st); // rechte und obere Kante
color = vec3(bl.x bl.y tr.x * tr.y); //UND-Verknuepfung
`step()` zum Vergleichen, eine Subtraktion für die Drehung der Koordinaten und die Multiplikation als logische UND-Verknüpfung.Bevor wir weitermachen, probiere bitte die folgenden Übungen aus:
* Ändere die Größe und die Proportionen des Rechtecks.
`step()` durch `smoothstep()`. Versuche, dadurch von harten Kanten zu weichen Kanten mit eleganten Übergängen zu gelangen.`floor()` verwendet.* Nimm die Implementierung, die Dir am besten gefällt, und baue daraus eine Funktion, die Du in Zukunft zum Zeichnen von Rechtecken und Quadraten verwenden kannst. Gestalte diese Funktion möglichst effizient.
* Entwickle eine andere Funktion, die kein ausgefülltes Rechteck zeichnet, sondern nur die Umrisse, d.h. die vier Kanten.
* Überlege Dir, wie Du die Rechtecke verschieben und mehrere davon auf der Zeichenfläche platzieren kannst. Falls Dir das gelingt, unterstreiche Deine Fähigkeiten, indem Du eine Komposition in der Art von Piet Mondrian erstellst.
Kreise
Es ist nicht schwer, Quadrate auf Millimeterpapier zu zeichnen und Rechtecke mit Hilfe kartesischer Koordinaten zu konstruieren. Kreise verlangen jedoch einen anderen Ansatz, zumal wie hier einen Pixel-orientierten Algorithmus benötigen. Eine Lösung besteht darin, die Koordinaten zu transformieren, damit wir beim Zeichnen die
`step()`-Funktion nutzen können.Doch wie soll das funktionieren? Lasse uns noch einmal zum Mathematikunterricht und dem Millimeterpapier zurückkehren. In der Schule haben die meisten von uns vermutlich gelernt, wie man mit dem Zirkel einen Kreis malt: Über das Drehrad stellt man zunächst den gewünschten Radius ein, platziert den Zirkel im Mittelpunkt des zu zeichnenden Kreises und schwingt das Zeichengerät anschließend mit einer eleganten Drehung über das Papier.
Will man diese Vorgehensweise auf ein Shader-Programm übertragen, bei dem jedes kleine Feld auf dem Millimeterpapier einem Pixel entspricht, muss man jedes Pixel (bzw. Thread) fragen, ob es zum Kreis gehört. Das machen wir, indem wir die Entfernung des Pixels zum Mittelpunkt des gewünschten Kreises berechnen.
Tatsächlich gibt es in GLSL mehrere Wege, diese Entfernung zu bestimmen. Die einfachste Möglichkeit greift auf die
`distance()`-Funktion zurück, die intern den Abstand (die Länge) `length()` zwischen zwei Punkten berechnet. In unserem Fall werden diese beiden Punkte durch die aktuelle Pixel-Koordinate sowie die Mitte der Zeichenfläche verkörpert, die hier den Kreismittelpunkt bilden soll. Die `length()`-Funktion ist nichts anderes als eine Umsetzung der Hypothenuse-Formel, die intern die Quadratwurzel (`sqrt()`) berechnet.`distance()`-Funktion, die `length()`-Funktion oder die `sqrt()`-Funktion nutzen, um die Entfernung zur Mitte der Zeichenfläche zu berechnen. Der folgende Programmcode enthält alle drei Möglichkeiten und liefert erwartungsgemäß jeweils das gleiche Ergebnis zurück.* Kommentiere die einzelnen Zeilen aus bzw. ein, um Dir die verschiedenen Wege anzuschauen.
<div class="codeAndCanvas" data="circle-making.frag"></div>
In dem obigen Beispiel bilden wir die Entfernung zum Mittelpunkt der Zeichenfläche auf die Helligkeit der Pixel ab. Je näher sich ein Pixel beim Mittelpunkt befindet, desto geringer (dunkler) ist sein Farbwert. Beachte bitte, dass die Pixel auch zum Rand hin nicht allzu hell werden, weil die Entfernung vom Mittelpunkt (
`vec2(0.5, 0.5)` ) zu den Rändern maximal `0.5` beträgt. Denke ein wenig über die Abbildung nach und überlege Dir:* Was kannst Du daraus ableiten?
* Wie kannst Du all dies nutzen, um einen Kreis zu malen?
* Verändere des obige Beispiel, damit der Farbverlauf ausschließlich innerhalb der Grenzen des Kreises stattfindet.
Distanzfelder
Man kann sich das obige Beispiel auch als eine Art Höhenprofil vorstellen, bei dem dunklere Stellen für größere Höhen stehen. Der Farbverlauf repräsentiert dann so etwas wie einen Kegel. Stell Dir vor, Du stehst auf der Spitze des Kegels. Die horizontale Entfernung zum Kegelrand beträgt in alle Richtungen jeweils
`0.5`. Indem Du den Kegel an einer gewählten Stelle abschneidest, erhältst Du je nachdem eine größere oder eine kleinere Kreisfläche.Im Prinzip nutzen wir also eine Neuinterpretation des Raumes (ausgehend vom Abstand zur Mitte), um eine bestimmte Form zu kreieren. Diese Technik ist als „Distanzfeld“ bekannt und wird bei der Erstellung von 3D-Grafiken auf vielfältige Weise genutzt.
Versuche Dich doch einmal an folgenden Übungen:
`step()`-Funktion, um alle Punkte größer als `0.5` weiß zu malen und alles darunter schwarz.* Invertiere die Farben von Vordergrund und Hintergrund.
`smoothstep()` ein und experimentiere mit verschiedenen Grenzwerten, um angenehm sanfte Übergänge am Rand Deines Kreises zu erzeugen.* Sobald Dir die Implementierung gefällt, baue daraus eine Funktion, die Du in zukünftigen Projekten einsetzen kannst.
* Fülle den Kreis mit einer Farbe.
* Kannst Du den Kreis animieren, so dass sein Umfang rhythmisch ansteigt und abschwillt, so wie bei einem schlagenden Herz? (Du kannst Dich dabei von der Animation aus dem letzten Kapitel anleiten lassen.)
* Wie sieht es mit der Bewegung des Kreises aus? Gelingt es Dir, den Kreis zu verschieben und mehrere Kreise innerhalb der Zeichenfläche erscheinen zu lassen?
* Was passiert, wenn Du mehrere Distanzfelder durch unterschiedliche Funktionen und Operationen kombinierst?
glslpct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
* Entwickle drei Kompositionen auf Basis dieser Technik. Wenn Du sie animieren kannst, umso besser!
####Für Deine Werkzeugsammlung
`sqrt()`-Funktion – und alle Funktionen, die darauf basieren – sehr kostspielig sein. Deshalb hier nun ein anderer Weg, wie man kreisförmige Distanzfelder mit Hilfe der `dot()`-Funktion über das Skalarprodukt erzeugen kann.<div class="codeAndCanvas" data="circle.frag"></div>
Nützliche Eigenschaften von Distanzfeldern
Distanzfelder lassen sich nutzen, um beinahe jede Art von Form zu zeichnen. Je komplexer die gewünschte Form, desto komplexer fällt in der Regel auch die dafür benötigte Distanzformel aus. Doch sobald Du die benötigte Distanzformel beisammenhast, wird es sehr einfach, gewünschte Effekte darauf anzuwenden, beispielweise weiche Kanten oder mehrfache Umrisse. Aus diesem Grund sind Distanzfelder u.a. bei der Schriftenerzeugung sehr populär, nachzulesen etwa bei Mapbox GL Labels, bei Matt DesLauriers, bei Material Design Fonts und in Chapter 7 of iPhone 3D Programming, O’Reilly.
Schau Dir den folgenden Programmcode an.
<div class="codeAndCanvas" data="rect-df.frag"></div>
Wir beginnen, indem wir den Wertebereich unserer x- und y-Ordinate auf die Spanne zwischen
`-1` und `1` skalieren und damit gleichzeitig die Basis des Koordinatensystems (0/0) in die Mitte der Zeichenfläche verschieben. In der Zeile 24 visualisieren wir die Werte unseres Distanzfeldes, indem wir sie mit zehn multiplizieren und die `fract()`-Funktion auf das Ergebnis anwenden. fract() liefert immer nur den Nachkommateil des Ergebnisses, also eine Zahl zwischen `0.0` und `0.99999...`, so dass bei den wiederholten Aufrufe für die verschiedenen Pixel ein Farbverlauf von Schwarz nach Weiß entsteht. Durch die Multiplikation mit zehn wiederholt sich dieser Verlauf genau zehn Mal. So entsteht ein repetitives Kreismuster, wie bei einem Zen-Garten.Lass uns einen Blick auf die Formel zur Berechnung des Distanzfeldes in der Programmzeile 19 werfen. Dort berechnen wir den Abstand zur Position
`(.3,.3)`. Damit dies in allen vier Quadranten gleichermaßen geschieht, wird der zu bearbeitende Punkt jeweils auf den ersten Quadranten abgebildet. Dafür sorgt hier die `abs()`-Funktion.Wenn Du die Kommentarzeichen in Programmzeile 20 entfernst, kannst Du beobachten, wie wir die Entfernung bei allen Punkten um
`0.3` reduzieren. Anschließend setzen wir den Abstand mit Hilfe der `min()`-Funktion für alle Punkte, deren Abstand dann noch größer als `0.0` ist (also vorher größer als `0.3` war), auf `0.0`. Das erzeugt ein neues interessantes Muster.Sobald Du anschließend die Kommentarzeichen aus der Programmzeile 21 entfernst, geschieht etwas ähnliches, nur quasi umgekehrt und mit Hilfe der Funktion
`max()`. Wieder wird der Abstand um `0.3` verringert, anschließend aber für alle Punkte auf `0.0` gesetzt, deren Abstand nun kleiner als `0.0` ist (vorher also zwischen `0.0` und `0.3` lag). Wir erhalten auf diese Weise mehrere geschachtelte Quadrate mit abgerundeten Ecken.Entferne nun nach und nach die Kommentarzeichen aus den Programmzeilen 27 bis 29, um deren Auswirkung auf das Zeichnen mit dem Distanzfeld zu verstehen.
Polarformen
Im Kapitel über die Verwendung von Farben haben wir kartesische Koordinaten auf Polarkoordinaten abgebildet. Wir berechneten dazu den Radius und den Winkel jeder Koordinate mit Hilfe der folgenden Formel:
glslvec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);
Einige dieser Formeln haben wir auch am Anfang dieses Kapitels genutzt, als es darum ging, Kreise zu zeichnen. Wir berechneten die Entfernung zum Kreismittelpunkt mit Hilfe der
`length()`-Funktion. Jetzt, wo wir Distanzfelder kennengelernt haben, öffnet sich uns ein weiterer Weg zum Zeichnen komplexer Formen mithilfe von Polarkoordinaten.Diese Technik unterliegt gewissen Beschränkungen, ist dafür aber sehr simpel und leistungsfähig. Sie beruht darauf, den Radius eines Kreises in Abhängigkeit des jeweiligen Winkels zu verändern, um unterschiedliche Formen zu erschaffen. Wie genau läuft diese Modulierung ab? Nun, Du hast es vielleicht schon erraten: Mit formgebenden Funktionen.
Unten findest Du verschiedene Funktionen jeweils zwei Mal: einmal als Verlaufskurve in einem kartesischen Koordinatensystem und dann als Shader-Programmcode in einem polaren Koordinatensystem. Dort stehen die verschiedenen formgebenden Funktionen in den Programmzeilen 21 bis 25. Entferne nun Schritt für Schritt die Kommentarzeilen und vergleiche den jeweiligen Funktionsgraphen im kartesischen Koordinatensystem mit seinem Äquivalent beim Zeichnen innerhalb eines Polarkoordinatensystem mit GLSL.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Versuche doch einmal:
* Diese Formen zu animieren.
* Verschiedene formgebende Funktionen zu kombinieren, um Löcher in die Formen zu „stanzen“ und dadurch Blumen, Schneeflocken oder Zahnräder entstehen zu lassen.
* Nutze die
`plot()`-Funktion aus dem Kapitel über formgebende Funktionen, um nur die Kontur der jeweiligen Form zeichnen zu lassen.Und nun alles zusammen
`atan()`-Funktion in Abhängigkeit des Winkels für das Zeichnen unterschiedlicher Formen zu nutzen. Nun können wir `atan()` auch mit Distanzfeldern einsetzen, um ganz unterschiedliche Effekte zu erzielen.Unser Trick nutzt die gegebene Anzahl der Seiten eines Polygons, um das benötigte Distanzfeld mit Hilfe von Polarkoordinaten zu erzeugen. Schau Dir dazu auch den folgenden Programmcode von Andrew Baldwin an.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Nutze das obige Programmbeispiel, um eine Funktion zu programmieren, die als Eingabe die Anzahl und Position der Ecken einer gewünschten Form erhält und als Ergebnis den Wert für das Distanzfeld liefert.
* Mische verschiedene Distanzfelder miteinander, indem Du die Funktionen
`min()` und `max()` nutzt.* Wähle ein geometrisches Logo, das Du mit Hilfe von Distanzfeldern replizierst.
Herzlichen Glückwunsch! Du hast Dich durch schwieriges Fahrwasser gekämpft. Nimm eine kleine Pause, damit sich das Erlernte setzen kann. Das Zeichnen komplexer Formen im Land der Shader ist wahrlich nicht ganz trivial, das kann durchaus ein wenig erschöpfen.
Unten am Ende dieses Kapitels finden Sie einen Link zu [PixelSpirit Deck] (https://patriciogonzalezvivo.github.io/PixelSpiritDeck/). Mit diesem Kartenspiel können Sie neue SDF-Funktionen erlernen, in Ihre Designs einfügen und verwenden auf deine Shader. Das Deck hat eine prägresive Lernkurve. Wenn Sie also eine Karte pro Tag nehmen und daran arbeiten, können Sie Ihre Fähigkeiten monatelang verbessern und herausfordern.
Da Du nun weißt, wie man unterschiedliche Formen zeichnet, kommen Dir bestimmt viele interessante Ideen in den Sinn. In den folgenden Kapiteln lernen wir, wie man Formen verschieben, skalieren und rotieren kann. Das wird Dir ermöglichen, komplexe Kompositionen zu erstellen.
---
07/README Fa (07/README-fa.md)
اشکال
بالاخره! برای این لحظه آماده شده ایم، شما بیشتر مبانی و توابع GLSL را آموخته اید. معادلات شکلی دهی را تمرین کرده اید، اکنون زمان جمع بندی است. شما برای این چالش آماده هستید! در این قسمت شما نحوه رسم اشکل ساده را بصورت موازی میآموزید.
مستطیل
تصور کنید کاغذی شبکه ای مانند آنچه در کلاس ریاضیات داشتیم در اختیار داریم، و تکلیف ما رسم مربع است، اندازه کاغذ 10x10 و مربع باید 8x8 باشد. شما چه کار خواهید کرد؟
شما به جز سطر های اول و اخر و ستون اول و آخر همه را رنگ میزنید، اینطور نیست؟
این مثال چه ربطی به شیدر ها دارد؟ هر مربع کوچک از کاغد شبکه مان مانند یک ترد(پیکسل) است. همچنین هر مربع کوچک مانند صفحات شطرنج مختصات خودر را میداند. در قسمت های قبلی ما رنگ های قرمز و سبز را به مقادیر x,y مپ کردیم، و یاد گرفتیم چگونه در مسائل دو بعدی از مقادیر بین 0.0 تا 1.0 استفاده کنیم، چگونه میتوان از این موضوع برای ترسیم یک مربع در وسط صفحه کنوس استفاده کرد؟
بیایید با استفاده از دستورات شبه کد شروع کنیم، یعنی دستورات if را استفاده کنیم. اصول انجام این کار به طرز چشم گیری شبیه سناریوی کاغذ شبکه ای است.
glslif ( (X GREATER THAN 1) AND (Y GREATER THAN 1) )
paint white
else
paint black
حالا که میدانیم چگونه کار میکند بیاید بجای استفاده از دستور شرطی از
step() استفاده کنیم, و همچنین بجای استفاده از 10*10 از مقادیر بین 0.0 و 1.0 استفاده کنیم.:glsluniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Each result will return 1.0 (white) or 0.0 (black).
float left = step(0.1,st.x); // Similar to ( X greater than 0.1 )
float bottom = step(0.1,st.y); // Similar to ( Y greater than 0.1 )
// The multiplication of left*bottom will be similar to the logical AND.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
step() پیکسل های زیر 0.1 را به سیاه تبدیل میکند (vec3(0.0)) و بقیه تبدیل به سفید میشوند (vec3(1.0)) . ضرب بین left و bottom مانند عمکرد and کار میکند، جایی که هردو باید 1.0 باشند، 1.0 برمیگرداند. این عمل باعث کشیدن دو خط در پایین و سمت چپ کنوس میشود..در کد قبلی ما ساختار را برای قسمت پایین و چپ تکرار کردیم. میتوان با پاس دادن یک بردار دو بعدی یه
step() چند خط در کد صرفه جویی کنیم. به این صورت :glslvec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;
تا الان فقط دو حاشیه پاین و چپ مستطیل را رسم کرده ایم. بیایید دو مورد دیگر را هم انجام دهیم. کد زیر را بررسی کنید:
<div class="codeAndCanvas" data="rect-making.frag"></div>
خطوط 21 و 22 را از کامنت خارج کنید و دقت کنید که چگونه مختصات St را معکوس کردیم و عملکرد Step را بصورت مشابه روی آن اعمال شد. در این حالت
vec2(0.0,0.0) بالا سمت راست کنوس خواهد بود. این یک مثال دیجیتالی از برگرداندن ورق کاغذ و استفاده مجدد عملیات است..توجه کنید در خط 18 و22 تمام طرفین در هم ضرب میشوند که معادل نوشتن عبارت زیر است:
glslvec2 bl = step(vec2(0.1),st); // bottom-left
vec2 tr = step(vec2(0.1),1.0-st); // top-right
color = vec3(bl.x bl.y tr.x * tr.y);
step() و ضرب و برگرداندن مختصات است.قبل از ادمه دادن سعی کنید تمرینات زیر را انجام دهید:
* اندازه و نسبت مستطیل را تغییر دهید.
smoothstep() بجای step() استفاده کنید. توجه کنید با تغییر مقادیر میتوانید از لبه های بلوری به حاشیه های صاف و ظریف بروید.floor() استفاده کند.* یک شکل مورد علاقه خود را پیاده سازی کنید، و تابعی در آن ایجاد کنید که در آینده هم بتوانید از آن استفاده مجدد کنید، عملکرد خود را انعطاف پذیر و کارامد کنید.
* ِیک تابع دیگر بسازید که فقط اضلاع مستطیل را نمایش دهد.
* فکر میکنید چگونه میتوان مستطیل های بیشتری در بیلبورد قرار داد و آن ها را متحرک ساخت? اگر راهش را پیدا کردید ترکیبی از مستطیل ها و رنگ های مختلف شبیه نقاشی Piet Mondrian بکشید.
دایره ها
ترسیم مربع و مستطیل روی کاغذ شبکه و مختصات دکارتی آسان بود. اما دایره ها به روشی دیگر احتیاج دارند، به خصوص که ما الگوریتمی لازم داریم که روی هر پیکسل به صورت جدا اجرا میشود. یک راه این است که مختصات را تغییر دهیم و از تابع
step() برای ترسیم دایره استفاده کنیم.چگونه? بیایید برگردیم به کلاس ریاضی و کاغذ شبکه ای مان, زمانی که پرگار را به اندازه شعاع دایره مان باز میکردیم, یکی از نقاط پرگار را به وسط دایره فشار میدادیم و بقیه را در امتداد آن دایره میچرخاندیم.
در شیدر ما برای هر پیکسل این سوال را میپرسیم، که آیا این پیکسل یا همان ترد در داخل منطقه دایره مورد نظر ما هست یا خیر. این کار را با محاسبه فاصله پیکسل تا مرکر دایره میتوان انجام داد..
راه های زیادی برای محاسبه فاصله وجود دارد. راحت ترین آن استفاده از
distance() است, که همان طول length() بین دو نقطه را محاسبه میکند (در مثال ما فاصله بین پیکسل مورد نظر و مرکز دایره). تابع length() کوتاه شده hypotenuse equation است، از جزر (sqrt()) استفاده میکند.distance(), length() یا sqrt() استفاده کنید. کد زیر حاوی هر 3 این عملکرد ها است، همانطور که پیش بینی میکنید هر یک نتیجه مشابهی را بر میگردانند.* خطوط را از حالت کامت خارج کنید و روش های دیگر را کامنت کنید تا نتیجه یکسان را مشاهده کنید .
<div class="codeAndCanvas" data="circle-making.frag"></div>
در مثال قبلی فاصله پیکسل تا مرکز کنوس میزان روشنایی را ترسیم میکند. هرچه پیکسل به مرکز نزدیکتر باشد، مقدار آن تیره و کمتر است. توجه کنید که مقادیر خیلی زیاد نمیشوند زیرا فاصله یک پیکسل از مرکز حداکثر کمی بیش از 0.5 است. این را در نظر بگیرید و فکر کنید:
* چه چیزی از آن میتوانید استنباط کنید?
* چگونه از این روش برای کشیدن دایره میتوان استفاده کرد?
* کد بالا را طوری تغییر دهید که تمام گرادیان دایره داخل کنوس باشد..
Distance field
میتوان مثال بالا را مانند یک نقشه ارتفاع در نظر گرفت، جایی که هر چه تاریک تر باشد یعنی بلند تر است. گرادیان تولید شده الگویی مانند مخروط را به ما نمایش میدهد، خود را در بالای آن مخروط در نظر بگیرید، فاصله افقی تا لبه ی مخروط برابر 0.5 است، این عدد از همه جهات ثابت است، با انتخاب محل برش مخروط، میتوان یک دایره در آن سطح بدست آورد.
اساس ما برای ساخت اشکال از تفسیر مجدد فضا در این مثال بر اساس فاصله تا مرکز، استفاده میکنیم. این تکنیک بعنوان“distance field” و به روش های گوناگون در outLine فونت ها تا گرافیک سه بعدی از آن استفاده میشود.
تمرین های زیر را انجام دهید:
step() استفاده کنید تا همه چیز بیش تر از 0.5 را به سفید و کمتر از 0.5 را به سایه 0.0 تبدیل کند.* رنگ بکگراند و فورگراند را عوض کنید.
smoothstep(), با مقادیر مختلف استفاده کنید تا یک مرز نرم برای دایره بسازید.* وقتی از نتیجه راضی بودید، از آن یک تابع بسازید تا بعدا بتوانبد استفاده کنید.
* به دایره رنگ اضافه کنید.
* آیا میتواندی مانند یک قلب تپنده، دایره خود را کوچک و بزرگ کنید؟ میتواندی از انیمیشن های فصل قبل الهام بگیرید.
* آیا میتوانید دایره را حرکت دهید؟ اضافه کردن دایره های بیشتر چطور؟
* اگر فیلد فاصله را با عملکرد های دیگر ترکیب کنید چه اتفاقی میافتد؟
glslpct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
* با استفاده از این تکنیک سه ترکیب بسازید، اگر به حالت انیمیشنی باشد که چه بهتر!
#### جعبه ابزار
sqrt() و توابعی که از آن استفاده میکنند، زیاد است. این یک راه دیگر برای ساخت فیلد فاصله دایره ای با استفاده از ضرب داخلی dot() است.<div class="codeAndCanvas" data="circle.frag"></div>
فواید دیگر فیلد(میدان) فاصله
با استفاده از فیلد فاصله تقریبا میتوان هر چیزی کشید. مشخصا هر چه شکل پیچیده تر باشد، معادله آن پیچیده تر خواهد بود، اما وقتی فرمول فیلد فاصله برای رسم یک شکل را بدست آورید، خیلی ساده میتوان آن را با افکت های دیگر ترکیب یا اضافه کرد، مانند لبه های نرم یا outLine ها . برای همین استفاده از فیلد فاصله در رندر کردن فونت ها بسیار رایج است, مانند Mapbox GL Labels, Matt DesLauriers Material Design Fonts و as is described in Chapter 7 of iPhone 3D Programming, O’Reilly.
نگاهی به کد زیر بیاندازید.
<div class="codeAndCanvas" data="rect-df.frag"></div>
برای شروع مختصات را به مرکز انتقال دادیم و برای مپ کردن مقادیر بین 1 و -1 آن را به نصف فشرده کردیم. همچنین در خط 24 ما فیلد فاصله را با استفاده از تابع
fract() نشان میدهیم، این تابع پترن ساخته شده را راحتر به نماش میگذارد. الگوی میدان فاصله مانند حلقه های باغ zen مکررا تکرار میشوند.بیایید نگاهی به فرمول فاصله فیلد در خط 19 بیاندازیم. در آنجا ما فاصله تا مکان (.3و.3) را در هر چهار ربع مختصات محاسبه میکنیم (به همین علت از قدر مطلق
abs() استفاده کردیم).اگر خط 20 را از کامنت خارج کنید، ترکیب تابع فاصله با این چهار نقطه به همراه
min() استفاده کردیم و الگویی جالب را بدست آوردیم.حال خط 21 را از کامنت خارج کنید، همان کار را انجام میدهیم اما اینبار توسط تابع
max() . نتیجه مستطیل هایی با گوشه های خمیده است. توجه کنید که حلقه ها هر چه دورتر هستند، نرم تر میشوند.خطوط 27 تا 29 را یک به یک از حالت کامنت خارج کنید و کاربرد های مختلف الگوی فاصله فیلد را درک کنید..
اشکال قطبی
در فصل رنگ ها با محاسبه شعاع و زاویه مختصات دکارتی را به قطبی تبدیل کردیم، از فرمول زیر استفاده کرده بودیم:
glslvec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);
در اول این فصل از قسمتی از این فرمول برای رسم دایره استفاده کردیم. با استفاده از
length() فاصله پیکسل تا مرکز را محاسبه میکردیم. حالا که در مورد فیلد فاصله میدانیم، میتوان از راه دیگری برای رسم اشکال در مختصاب قطبی استفاده کرد.این روش کمی محدود کننده، اما بسیار ساده است. این روش از تغییر شعاع دایره بسته به زاویه برای رسم اشکال مختلف استفاده میکند. اما چگونه؟ با استفاده از توابع شکل دهی.
در خطوط 21 تا 25 مثال زیر همان توابع در مختصات دکارتی را در مختصات قطبی مشاهده میکنید. یک به یک خط ها را از حالت کامنت خارج کنید، به رابطه بین مختصات های مختلف توجه کنید.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
سعی کنید:
* این اشکال را انیمیت کنید.
* توابع مختلف شکل دهی را ترکیب کنید تا برشی وسط شکل بتوانید ایجاد و اشکالی مثل گل، دانه برف و چرخ دنده ایجاد کنید.
* از تابع
plot() که در فصل توابع شکل دهی ساختیم استفاده کنید تا فقط حد فاصل دور اشکال را رسم کنید.ترکیب قدرت ها
اکنون که با استفاده از atan() یاد گرفتیم چگونه شعاع دایره را با استفاده از زاویه آن تنظیم کنیم و اشکال مختلف بسازیم, حال میتوان از atan() با فیلد فاصله استفاده کرد و تکنیک های که یاد گرفیم روی آن استفاده کرد..این ترفند از لبه های چند ضلعی برای ساخت فیلد فاصله با استفاده از مختصات قطبی استفاده میکند. این کد از Andrew Baldwin را نگاهی بیاندازید.
<div class="codeAndCanvas" data="shapes.frag"></div>
* با استفاده از این مثال تابعی ایجاد کنید، که مقدار گوشه ها و موقعیت یک شکل را دریافت و یک فیلد فاصله برگرداند.
min() و max() با هم ترکیب کنید.* یک لوگو هندسی را انتخاب کنید، و سعی کنید آن را با استفاده از فیلد فاصله بسازید.
تبریک! بخش سختی را گذراندید! کمی استراحت کنید و اجازه دهید این مفاهیم جا بیوفتد، ساخت اشکال در نرم افزار Processing آسان است، ولی اینجا خیر. در سرزمین شیدر ها طراحی شکل پیچیده است، و سازگاری با این الگوی جدید میتواند طاقت فرسا باشد!
در آخر این صفحه به شما PixelSpirit Deck معرفی شده. این کارت ها به شما توابع شکل دهی را بخوبی یاد میدهند. از آن ها در طرح ها و شیدر های خودتان استفاده کنید. این دسته کارت دارای یک منحنی یادگیری تدریجی است. بنابر این تمرین کردن یک کارت در هر روز میتواند تا 30 روز شما را به چالش بکشد.
اکنون که میدانید چگونه اشکال را رسم کنید، مطمئنم ایده های جدید در ذهنتان ایجاد میشود. در فصل بعدی، نحوه حرکت، چرخش و مقیاس پذیری اشکال را خواهید آموخت. و امکان ترکیب آنان را خواهید داشت.
---
07/README Fr (07/README-fr.md)
Formes
Enfin ! Tous ce que nous avons appris nous a mené jusqu'à ce moment !
Nous avons vu la plupart des bases, des types et des fonctions,
nous avons répété nos fonctions de formes à une dimension, il est temps de faire marcher tout ça ensemble et vous êtes paré !
Dans ce chapitre, vous apprendrez à dessiner des formes, de façon procédurale et en parallèle sur un GPU.
Rectangle
Imaginons que nous avons un papier millimétré, comme à l'école, et nos devoirs consistent à dessiner un carré.
La taille de la feuille est 10x10 et le carré doit mesurer 8x8, comment faire ?
A priori nous allons colorier tout sauf : la première et la dernière rangée et la première et la dernière colonne, c'est bien cela ?
En quoi est-ce lié aux shaders ?
Chaque petit carré du papier millimétré est un thread (un pixel, ou fragment).
chaque petit carré connaît sa position, comme des coordonnées sur un échiquier.
Dans les chapitres précédents, nous avons appris à nous servir des valeurs normalisées, par exemple, nous avons mappé ces positions x et y vers les canaux rouge et vert.
Comme nos valeurs étaient normalisées entre 0.0 and 1.0, on pouvait les utiliser comme des couleurs, ou dans les fonctions de formes, ou dans les interpolations.
Mais à présent, comment utiliser ces valeurs x et y normalisées pour dessiner un carré au centre du canvas ?
Commençons par utiliser un pseudocode se servant d'une condition
if/else sur toute la taille du canvas.
Le procédé est très proche de la démarche que nous avons eu avec le papier millimétré.glslif ( (X SUPERIEUR A 1) AND (Y SUPERIEUR A 1) )
dessine en blanc
else
dessine en noir
Nous avons une meilleure idée du code qu'il va falloir produire, nous allons remplacer les
if par des step()
et au lieu d'utiliser 10x10, nous utiliserons les valeurs x et y normalisées entre 0.0 et 1.0 :glsluniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// chaque appel à step() renverra soit: 1.0 (blanc), soit 0.0 (noir).
float gauche = step(0.1,st.x); // équivalent à: si( X supérieur à 0.1 )
float bas = step(0.1,st.y); // équivalent à: si( Y supérieur à 0.1 )
// multiplier gauche par bas revient à faire un AND logique.
color = vec3( gauche * bas );
gl_FragColor = vec4(color,1.0);
}
step(), va dessiner tous les pixels dont la valeur des x est inférieure à 0.1 en noir (vec3(0.0)) et tous les autres en blanc (vec3(1.0)).
Le fait de multiplier gauche par bas est équivalent à l'opérateur logique AND ; si les deux opérantions (x<.1 et y<.1) renvoient 1.0, le résultat sera 1.0, sinon, ce sera 0.0.
Cela nous permet de dessiner 2 lignes noires, une en bas et une à gauche du canvas.En règle générale et bien que ce ne soit pas interdit, il est déconseillé d'utiliser les
if dans un shader.
ça peut paraître contre-intuitif mais la raison est simple, si on fait un if (ce qu'on appelle un conditional branching),
le programme va devoir évaluer les 2 branches de toutes façons et cette évaluation va ralentir (voire anéantir) le bénifice d'utiliser le GPU.
Une stratégie pour parer à ce problème est de structurer le code de manière à éliminer les conditions, donc les branches.
En l'occurrence, se servir du résultat (0 ou 1) de step() et le multiplier par une autre variable (la couleur par exemple).
Si le step() renvoie 1, la couleur se multipliera par 1 et restera la même, si le step() renvoie 0, la couleur se multipliera par 0 donc elle passe au noir.
C'est une technique que vous retrouverez souvent dans les shaders.Dans l'exemple ci-dessus, nous répétons la même opération sur chaque axe gauche et bas.
Nous pouvons économiser quelques lignes de code en passant les deux valeurs x et y au
step() simultanément au lieu de faire deux appels à step() séparés, ce qui resemble à ça :glslvec2 limites = step(vec2(0.1),st);
float pct = limites.x * limites.y;
Nous avons à présent deux limites de notre rectangle : gauche et bas. Occupons nous des deux autres : haut et droite, regardez le code suivant :
<div class="codeAndCanvas" data="rect-making.frag"></div>
Décommentez les lignes 21-22 et notez comment nous inversons les coordonnées de
st (1.0 - st) et réutilisons le même appel à step().
Le vec2 tr contient à présent les réponses du test pour le coin haut droit. C'est l'équivalent numérique de : "retourner la page et appliquer la même procédure".
Notez qu'aux lignes 18 et 22, tous les côtés sont multipliés entre eux, ce qui revient à écrire :
glslvec2 bl = step(vec2(0.1),st); // bas-gauche
vec2 tr = step(vec2(0.1),1.0-st); // haut-droit
color = vec3(bl.x bl.y tr.x * tr.y);
Intéressant n'est ce pas ?
Le principe de cette technique est de se servir de
step() et des multiplications comme d'opérateurs logiques et de retourner les coordonnées pour traiter les deux côtés de l'image.Avant d'aller plus loin, essayez les choses suivantes :
* Changer la taille et les proportions du rectangle.
smoothstep() au lieu de step(). Notez comme les arêtes passent d'un rendu net à flou.floor(). Conservez votre version préférée et faites-en une version réutilisable, si possible une fonction flexible et efficace (pas de if* par exemple).
* Créez une fonction qui dessine uniquement l'extérieur du rectangle.
* Comment placer et déplacer plusieurs rectangles sur le canvas ? Si vous trouvez la réponse, tentez de créer une composition à la Piet Mondrian.
Cercles
Dessiner des carrés sur du papier millimétré est assez simple mais les cercles demandent un changement d'approche, notamment lorsqu'on doit traiter une masse de pixels.
Une solution est de re-mapper le système de coordonnées de manière à pouvoir utiliser la fonction
step() pour dessiner un cercle.Comment ?
Retournons à notre cours de math et à notre papier millimétré, on utilise un compas, on lui donne le bon rayon, on pose la pointe au centre du cercle et on trace le contour du cercle.
Traduire cette démarche dans un shader où chaque carré du papier est un pixel revient à demander à chaque pixel (ou thread ou fragment), s'il est à l'intérieur ou à l'extérieur du cercle.
Pour ce faire, nous allons donc calculer la distance de chaque fragment au centre de notre cercle.
Il existe plusieurs façons de calculer une distance. La plus simple est d'utiliser la fonction
distance(), qui - en interne - calcule la longueur de la différence entre les deux points passés en arguments (dans notre cas, les coordonnées du pixel et la position du centre).
La fonction length() est simplement un raccourci pour l'équation de l'hypoténuse qui utilise la racine carrée sqrt() de la somme des différences en x et y, au carré.distance(), length() ou sqrt() pour calculer la distance du pixel au centre du canvas.
Le code suivant contient ces trois fonctions et démontre sans surprise que les trois renvoient le même résultat.* Commentez et décommentez les blocs pour utiliser les différentes manières de calculer la distance.
<div class="codeAndCanvas" data="circle-making.frag"></div>
Dans l'exemple ci-dessus, nous mappons la distance au centre du canvas sur la valeur (la luminosité) du pixel de sortie.
Plus on est proche du centre, moins la distance est grande donc plus le pixel est sombre.
Notez que les valeurs ne montent pas énorménent (jusqu'au blanc par exemple) parce que, la plus grande distance entre un pixel et le centre (
vec2(0.5, 0.5)) dépasse péniblement 0.5.
Prenez un moment pour observer et demandez vous :
* Que peut-on déduire de ça?
* Comment peut-on s'en servir pour dessiner un cercle?
* Modifiez le code suivant pour faire tenir le dégradé complet du cercle à l'intérieur du canvas.
Note : Pour être exact, la plus grande distance entre un pixel et le centre est :
sqrt( 2.0 ) .5 soit environ 0.7071* !Champ de distance (Distance field)
Nous pouvons penser l'exemple ci-dessus comme étant une carte d'élévations ou les parties sombres seraient plus élevées.
Le dégradé du cercle ressemble à ce que serait un cone.
Imaginez vous au sommet de ce cone, la distance horizontale vers les bords du cone serait 0.5, elle est constante dans toutes les directions.
En choisissant à quelle hauteur on peut couper le cone, on obtiendra un disque plus ou moins grand.
En somme, on utilise une ré-interprétation de l'espace, en se basant sur la distance au centre plutôt que sur les coordonnées des pixels, pour créer des formes.
Cette technique s'appelle un "champ de distances" (Distance Field) et s'applique dans de nombreux contextes allant du dessin de contours à la 3D.
Essayez les choses suivantes:
* Utilisez step() pour passer toutes les valeurs supérieures à 0.5 en blanc et toutes les autres en noir.
* Inverser les couleurs d'avant plan et d'arrière plan.
* Utilisez smoothstep() et changez les paramètres pour ajouter un contour flou au cercle.
* Quand vous obtenez un résultat satisfaisant, créez une fonction que vous pourrez réutiliser.
* Ajoutez de la couleur.
* Pouvez-vous animer le cercle pour qu'il grossisse et rapetisse ? Pour qu'il simule un battement de coeur ? Les animations du chapitre précédent vous aideront !
* Comment déplacer ce cercle ? Pouvez vous le déplacer et placer plusieurs cercles sur le même canvas ?
* Que se passe-t'il quand on combine plusieurs champs de distances en utilisant différentes fonctions et opérations ?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Faites trois compositions avec ces techniques, si elles bougent, c'est encore mieux !
#### Pour la boîte à outils
La fonction sqrt() - comme toutes les fonctions qui en dépendent - est assez gourmande en ressources.
Voici autre une technique permettant de créer un champ de distances basée sur la fonction dot().
<div class="codeAndCanvas" data="circle.frag"></div>
dot() renvoie le produit scalaire de deux vecteurs: dot(a,b) = a.x b.x + a.y b.y;
Propriétés utiles des champs de Distances
Les champs de distance permettent de dessiner à peu près n'importe quoi.
Evidemment, plus la forme est complexe, plus l'équation sera complexe mais une fois qu'on a la formule d'une forme en particulier, il devient assez simple de la combiner avec d'autres et/ou de lui appliquer des effets comme des bords lissés, ou plusieurs contours.
C'est pourquoi les champs de distances sont utilisés pour le rendu des polices de caractères: Mapbox GL Labels, Matt DesLauriers, Material Design Fonts et comme expliqué au chapitre 7 de "iPhone 3D Programming", O’Reilly.
Prenez le code suivant:
<div class="codeAndCanvas" data="rect-df.frag"></div>
On commence par déplacer le système de coordonnées au centre et à le diviser par deux pour obtenir des valeurs comprises entre -1 et 1.
À la ligne 24, nous visualisons le champ de distances grâce à la fonction fract() ce qui nous permet de mieux voir le motif qu'il crée.
Le motif du champ de distances se répète en cercles concentriques, à la manière d'un jardin zen.
Regardons la formule du champ de distances ligne 19. Nous calculons la distance entre chaque position et la coordonnée ( .3,.3 ) ( entre st et le vecteur vec2(.3)) pour chaque quadrant, c'est à ça que sert l'appel à abs().
Si vous décommentez la ligne 20, vous noterez que nous combinons les distances à ces 4 points en utilisant la fonction min() contre 0, ce qui produit un nouveau motif.
L'idée est qu'il n'y a qu'un seul point, et non '4', celui en haut à droite mais il est reflété dans les 3 autres quadrants du fait qu'on a changé la taille de l'espace ligne 16 !
Essayez à présent de décommenter la ligne 21, nous utilisons cette fois la fonction max().
Le résultat est un rectangle aux bords arrondis.
Remarquez comme les anneaux du champ de distance deviennent de plus en plus lissse à mesure qu'ils s'éloignent du centre.
Enfin, décommentez les ligne 27 à 29 une par une pour comprendre les différentes utilisations du champ de distances.
Formes polaires
Au chapitre des couleurs, nous avons projeté (mappé) des coordonnées cartésiennes sur des coordonnées polaires en calculant un rayon et un angle entre chaque pixel et le centre grâce à la formule suivante:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);Nous avons ré-utilisé une partie de cette formule au début du chapitre pour dessiner un cercle.
Nous calculions la distance en nous servant de length().
Maintenant que nous en savons plus sur les champs de distances, nous pouvons dessiner de nouvelles formes grâce aux coordonnées polaires.
La technique est un peu restrictive mais très simple : elle consiste à changer la valeur du rayon en fonction de l'angle pour obtenir une variété de formes.
Comment moduler cette longueur ? Avec des fonctions de formes bien sûr !
Ci-dessous, vous trouverez les mêmes fonctions dans un espace cartésien et dans un espace polaire (entre les lignes 21 et 25 du shader).
Décommentez les fonctions une par une dans les deux démos pour comprendre les relations qui existent entre les deux systèmes de coordonnées.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Essayez de :
* Animer les formes.
Combiner différentes fonctions de formes pour creuser des trous* dans les formes pour faire des fleurs, des flocons et des engrenages.
* Utilisez la fonction plot() du chapitre sur les fonctions de forme pour ne garder que le contour.
Combinatoires
Nous venons de voir comment moduler le rayon d'un cercle en fonction d'un angle en utilisant la fonction atan() pour dessiner différentes formes.
Voyons maintenant comment utiliser atan() avec un champ de distances de façon à pouvoir utiliser les effets qu'ils permettent.
L'astuce c'est d'utiliser le nombre de côtés d'un polygone régulier pour construire un champ de distances dans un espace polaire.
Pour plus d'informations, vous pouvez vous référer au code suivant par Andrew Baldwin.
<div class="codeAndCanvas" data="shapes.frag"></div>
* En reprenant cet exemple, créez une fonction qui reçoit une position et un nombre de côtés et retourne la valeur du champ de distance correspondant.
* Mélangez les champs de distances en utilisant min() et max().
* Choisissez un logo géométrique et reproduisez le avec des champs de distance.
Félicitations !
Vous avez fait le plus dur !
Faites une pause et laissez décanter ces nouveaux concepts.
Dessiner des formes dans une API de dessin, c'est facile mais ici c'est une autre histoire.
Au pays des shaders, dessiner des formes géométriques est un peu tordu et s'imposer la disciple nécessaire à la compréhension de ce paradigme est épuisant.
À la fin de ce chapitre, vous trouverez un lien vers [PixelSpirit Deck] (https://patriciogonzalezvivo.github.io/PixelSpiritDeck/). Ce jeu de cartes vous aidera à apprendre les nouvelles fonctions SDF, à les composer dans vos conceptions et à les utiliser. sur vos shaders. La plate-forme a une courbe d’apprentissage prégressive, aussi prendre une carte par jour et travailler dessus vous poussera et mettra au défi vos compétences pendant des mois.
Maintenant que vous savez comment dessiner des formes, je suis sûr que ça va vous donner des idées.
Au prochain chapitre, nous apprendrons à déplacer, à appliquer des rotations et à changer d'échelle pour créer des compositions !
---
07/README Id (07/README-id.md)
Bentuk
Akhirnya! Kami telah membangun keterampilan untuk saat ini! Anda telah mempelajari sebagian besar fondasi, jenis, dan fungsi GLSL. Anda telah mempraktikkan persamaan pembentuk Anda berulang kali. Sekaranglah waktunya untuk menggabungkan semuanya. Anda siap untuk tantangan ini! Dalam bab ini Anda akan belajar cara menggambar bentuk sederhana dengan cara prosedural paralel.
Persegi panjang
Bayangkan kita memiliki kertas grid seperti yang kita gunakan di kelas matematika dan pekerjaan rumah kita adalah menggambar persegi. Ukuran kertas adalah 10x10 dan persegi seharusnya 8x8. Apa yang akan kamu lakukan?
Anda akan mengecat semuanya kecuali baris pertama dan terakhir dan kolom pertama dan terakhir, bukan?
Bagaimana ini berhubungan dengan shader? Setiap kotak kecil dari kertas kisi kami adalah thread (piksel). Setiap kotak kecil mengetahui posisinya, seperti koordinat papan catur. Di bab sebelumnya, kita memetakan x dan y ke saluran warna merah dan hijau, dan kita belajar cara menggunakan wilayah dua dimensi yang sempit antara 0,0 dan 1,0. Bagaimana kita bisa menggunakan ini untuk menggambar kotak di tengah di tengah papan reklame kita?
Mari kita mulai dengan membuat sketsa pseudocode yang menggunakan pernyataan if di atas bidang spasial. Prinsip untuk melakukan ini sangat mirip dengan bagaimana kita memikirkan skenario kertas grid.
uniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Each result will return 1.0 (white) or 0.0 (black).
float left = step(0.1,st.x); // Similar to ( X greater than 0.1 )
float bottom = step(0.1,st.y); // Similar to ( Y greater than 0.1 )
// The multiplication of left*bottom will be similar to the logical AND.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
Fungsi step() akan mengubah setiap piksel di bawah 0,1 menjadi hitam (vec3(0,0)) dan sisanya menjadi putih (vec3(1.0)). Perkalian antara left dan bottom berfungsi sebagai operasi logika AND, di mana keduanya harus bernilai 1,0 untuk mengembalikan 1,0. Ini menggambar dua garis hitam, satu di bagian bawah dan yang lainnya di sisi kiri kanvas.
Pada kode sebelumnya kami mengulangi struktur untuk setiap sumbu (kiri dan bawah). Kita dapat menyimpan beberapa baris kode dengan meneruskan dua nilai secara langsung ke step() alih-alih satu. Itu terlihat sperti ini:
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;Sejauh ini, kita hanya menggambar dua garis tepi (kiri bawah) dari persegi panjang kita. Mari lakukan dua lainnya (kanan atas). Lihat kode berikut:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Hapus tanda komentar baris 21-22 dan lihat bagaimana kita membalik koordinat st dan ulangi fungsi step() yang sama. Dengan begitu, vec2(0.0,0.0) akan berada di pojok kanan atas. Ini adalah persamaan digital dengan membalik halaman dan mengulangi prosedur sebelumnya.
Perhatikan bahwa dalam baris 18 dan 22 semua sisinya akan dikalikan. Ini sama dengan menulis:
vec2 bl = step(vec2(0.1),st); // bottom-left
vec2 tr = step(vec2(0.1),1.0-st); // top-right
color = vec3(bl.x bl.y tr.x * tr.y);Menarik kan? Teknik ini adalah tentang penggunaan step()
dan perkalian untuk operasi logika dan membalik koordinat.Sebelum melanjutkan, cobalah latihan berikut:
* Ubah ukuran dan proporsi persegi panjang.
* Lakukan percobaan dengan kode yang sama tetapi menggunakan smoothstep () daripada step(). Perhatikan bahwa dengan mengubah nilai, Anda dapat beralih dari tepi yang buram ke tepi halus yang elegan.
* Lakukan implementasi lain yang menggunakan floor().
* Pilih implementasi yang paling Anda sukai dan buat fungsinya yang dapat Anda gunakan kembali di masa mendatang. Jadikan fungsi Anda fleksibel dan efisien.
* Buat fungsi lain yang hanya menggambar garis bentuk persegi panjang.
* Menurut Anda, bagaimana Anda dapat memindahkan dan menempatkan persegi panjang yang berbeda di papan iklan yang sama? Jika Anda tahu caranya, tunjukkan keahlian Anda dengan membuat komposisi persegi panjang dan warna-warni yang menyerupai lukisan Piet Mondrian.
Lingkaran
Sangat mudah untuk menggambar kotak pada kertas kisi dan persegi panjang pada koordinat kartesius, tetapi lingkaran memerlukan pendekatan lain, terutama karena kita memerlukan algoritme "per piksel". Salah satu solusinya adalah dengan memetakan ulang koordinat spasial sehingga kita dapat menggunakan fungsi step() untuk menggambar lingkaran.
Bagaimana? Mari kita mulai dengan kembali ke kelas matematika dan kertas kisi, di mana kita membuka kompas ke jari-jari lingkaran, menekan salah satu titik kompas di tengah lingkaran dan kemudian menelusuri tepi lingkaran dengan putaran sederhana.
Menerjemahkan ini ke shader di mana setiap kotak pada kertas kisi adalah piksel menyiratkan menanyakan setiap piksel (atau thread) apakah itu di dalam area lingkaran. Kami melakukan ini dengan menghitung jarak dari piksel ke pusat lingkaran.
Ada beberapa cara untuk menghitung jarak tersebut. Yang termudah menggunakan fungsi distance(), yang secara internal menghitung length() dari perbedaan antara dua titik (dalam kasus kami koordinat piksel dan pusat kanvas). Fungsi length() tidak lain adalah jalan pintas dari persamaan sisi miring yang menggunakan akar kuadrat (sqrt()) secara internal.
Anda dapat menggunakan distance(), length() atau sqrt() untuk menghitung jarak ke pusat billboard. Kode berikut berisi tiga fungsi ini dan fakta yang tidak mengejutkan bahwa masing-masing mengembalikan hasil yang persis sama.
* Baris komentar dan hapus komentar untuk mencoba berbagai cara untuk mendapatkan hasil yang sama.
<div class="codeAndCanvas" data="circle-making.frag"></div>
Pada contoh sebelumnya kami memetakan jarak ke tengah papan reklame dengan kecerahan warna piksel. Semakin dekat sebuah piksel ke tengah, nilai yang lebih rendah (lebih gelap) dimilikinya. Perhatikan bahwa nilainya tidak terlalu tinggi karena dari pusat (vec2(0,5, 0,5)) jarak maksimum hampir tidak melebihi 0,5. Renungkan peta ini dan pikirkan:
* Apa yang dapat Anda simpulkan darinya?
* Bagaimana kita bisa menggunakan ini untuk menggambar lingkaran?
* Ubah kode di atas untuk memuat seluruh gradien melingkar di dalam kanvas.
Bidang jarak
Kita juga bisa menganggap contoh di atas sebagai peta ketinggian, di mana lebih gelap berarti lebih tinggi. Gradien menunjukkan kepada kita sesuatu yang mirip dengan pola yang dibuat oleh kerucut. Bayangkan diri Anda berada di atas kerucut itu. Jarak horizontal ke tepi kerucut adalah 0,5. Ini akan konstan ke segala arah. Dengan memilih tempat untuk “memotong” kerucut, Anda akan mendapatkan permukaan lingkaran yang lebih besar atau lebih kecil.
Pada dasarnya kami menggunakan interpretasi ulang ruang (berdasarkan jarak ke pusat) untuk membuat bentuk. Teknik ini dikenal sebagai "bidang jarak" dan digunakan dalam berbagai cara dari garis font hingga grafik 3D.
Coba latihan berikut:
* Gunakan step() untuk mengubah semua yang di atas 0,5 menjadi putih dan semua yang di bawah menjadi 0,0.
* Membalikkan warna latar belakang dan latar depan.
* Menggunakan smoothstep(), bereksperimenlah dengan nilai yang berbeda untuk mendapatkan batas halus yang bagus di lingkaran Anda.
* Setelah Anda puas dengan implementasinya, buatlah fungsinya yang dapat Anda gunakan kembali di masa mendatang.
* Tambahkan warna pada lingkaran.
* Bisakah Anda menganimasikan lingkaran Anda untuk tumbuh dan menyusut, menirukan detak jantung? (Anda bisa mendapatkan inspirasi dari animasi di bab sebelumnya.)
* Bagaimana dengan memindahkan lingkaran ini? Bisakah Anda memindahkannya dan menempatkan lingkaran yang berbeda dalam satu papan iklan?
* Apa yang terjadi jika Anda menggabungkan bidang jarak bersama menggunakan fungsi dan operasi yang berbeda?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Buat tiga komposisi menggunakan teknik ini. Jika dianimasikan, bahkan lebih baik!
#### Untuk kotak alat Anda
Dalam hal daya kompthreadi, fungsi sqrt() - dan semua fungsi yang bergantung padanya - bisa jadi mahal. Berikut adalah cara lain untuk membuat bidang jarak melingkar dengan menggunakan perkalian dot().
<div class="codeAndCanvas" data="circle.frag"></div>
Properti yang berguna dari Bidang Jarak
Bidang jarak dapat digunakan untuk menggambar hampir semua hal. Jelas, semakin kompleks suatu bentuk, semakin rumit persamaannya, tetapi setelah Anda memiliki rumus untuk membuat bidang jarak dari bentuk tertentu, sangat mudah untuk menggabungkan dan / atau menerapkan efek padanya, seperti tepi halus dan beberapa garis tepi . Karenanya, kolom jarak menjadi populer dalam rendering font, seperti Label GL Kotak Peta, Matt DesLauriers Font Desain Material dan seperti yang di jelaskan di Bab 7 di buku Phone 3D Programming, O’Reilly.
Perhatikan kode berikut.
<div class="codeAndCanvas" data="rect-df.frag"></div>
Kita mulai dengan memindahkan sistem koordinat ke tengah dan menyusutkannya menjadi dua untuk memetakan kembali nilai posisi antara -1 dan 1. Juga pada baris 24 kita memvisualisasikan nilai bidang jarak menggunakan fract() berfungsi memudahkan untuk melihat pola yang mereka buat. Pola bidang jarak berulang seperti cincin di taman Zen.
Mari kita lihat rumus bidang jarak pada baris 19 . Di sana kita menghitung jarak ke posisi pada (.3, .3) atau vec3 (.3) di keempat kuadran (itulah yang abs() melakukannya di sana).
Jika Anda menghapus komentar baris 20, Anda akan melihat bahwa kami menggabungkan jarak ke empat titik ini menggunakan min() ke nol. Hasilnya menghasilkan pola baru yang menarik.
Sekarang coba hapus komentar baris 21; kita melakukan hal yang sama tetapi menggunakan fungsi max(). Hasilnya adalah persegi panjang dengan sudut membulat. Perhatikan bagaimana cincin bidang jarak semakin halus semakin jauh dari pusat.
Selesaikan tanda komentar baris 27 hingga 29 satu per satu untuk memahami perbedaan penggunaan pola bidang jarak.
Bentuk kutub
Pada bab tentang warna kita memetakan koordinat kartesian ke koordinat kutub dengan menghitung radius dan sudut dari setiap piksel dengan rumus berikut:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);Kami menggunakan bagian dari rumus ini di awal bab untuk menggambar lingkaran. Kami menghitung jarak ke pusat menggunakan length(). Sekarang kita tahu tentang bidang jarak, kita dapat mempelajari cara lain untuk menggambar bentuk menggunakan koordinat kutub.
Teknik ini sedikit membatasi tetapi sangat sederhana. Ini terdiri dari mengubah jari-jari lingkaran tergantung pada sudut untuk mencapai bentuk yang berbeda. Bagaimana cara kerja modulasi? Ya, menggunakan fungsi pembentukan!
Di bawah ini Anda akan menemukan fungsi yang sama dalam grafik kartesius dan dalam contoh shader koordinat kutub (antara baris 21 dan 25 ). Hapus tanda komentar fungsi satu per satu, perhatikan hubungan antara satu sistem koordinat dan sistem koordinat lainnya.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Cobalah untuk:
* Animasikan bentuk-bentuk ini.
Gabungkan berbagai fungsi pembentukan untuk memotong lubang* dalam bentuk untuk membuat bunga, kepingan salju, dan roda gigi.
Gunakan fungsi plot() yang kami gunakan di Shaping Functions Chapter* untuk menggambar kontur saja.
Menggabungkan kekuatan
Sekarang kita telah mempelajari cara memodulasi jari-jari lingkaran sesuai dengan sudut menggunakan atan() untuk menggambar berbagai bentuk, kita dapat mempelajari cara menggunakan atan() dengan bidang jarak dan menerapkan semua trik dan efek yang mungkin dilakukan dengan bidang jarak.
Triknya akan menggunakan jumlah tepi poligon untuk membuat bidang jarak menggunakan koordinat kutub. Lihat kode berikut dari Andrew Baldwin.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Menggunakan contoh ini, buat fungsi yang memasukkan posisi dan jumlah sudut dari bentuk yang diinginkan dan mengembalikan nilai bidang jarak.
* Campurkan bidang jarak bersama-sama menggunakan min() dan max().
* Pilih logo geometris untuk direplikasi menggunakan bidang jarak.
Selamat! Anda telah berhasil melewati bagian yang sulit! Beristirahatlah dan biarkan konsep ini menyelesaikan - menggambar bentuk sederhana dalam Pemrosesan itu mudah tetapi tidak di sini. Dalam bentuk gambar tanah shader dipelintir, dan dapat melelahkan untuk beradaptasi dengan paradigma baru pengkodean ini.
Di bagian akhir bab ini, Anda akan menemukan tautan ke PixelSpirit Deck setumpuk kartu ini akan membantu Anda mempelajari fungsi SDF baru, menyusunnya ke dalam desain dan menggunakannya di shader Anda. Dek memiliki kurva belajar progresif, jadi mengambil satu kartu sehari dan mengerjakannya akan mendorong dan menantang keterampilan Anda selama berbulan-bulan.
Sekarang Anda tahu cara menggambar bentuk, saya yakin ide-ide baru akan muncul di benak Anda. Di bab berikut, Anda akan mempelajari cara memindahkan, memutar, dan mengatur skala bentuk. Ini akan memungkinkan Anda membuat komposisi!
---
07/README It (07/README-it.md)
Figure
Finalmente! Abbiamo costruito e allenato le nostre abilità per questo momento! Avete imparato la maggior parte delle basi, dei tipi e delle funzioni in GLSL. Vi siete esercitati continuamente sulle equazioni di forma. Ora è arrivato il momento di collegare tutti gli elementi. Siete all’altezza di questa sfida! In questo capitolo imparerete a disegnare forme semplici con una procedura in parallelo.
Rettangolo
Immaginate di avere una carta millimetrata, come quelle che si usano per le lezioni di matematica, e che il nostro compito sia quello di disegnare un quadrato. Le dimensioni del foglio sono 10x10 e il quadrato dovrebbe essere 8x8. Cosa fareste?
Colorereste tutto tranne la prima e l’ultima riga e la prima e l’ultima colonna, giusto?
Come si collega ciò agli shaders? Ogni piccolo quadrato della vostra carta millimetrata è un thread (un pixel). Ogni piccolo quadretto conosce la propria posizione, come per le coordinate di una scacchiera. Nei capitoli precedenti abbiamo associato x e y ai canali di colore rosso e verde, e abbiamo imparato come usare il limitato spazio bidimensionale tra 0.0 e 1.0. Come possiamo utilizzare tutto questo per disegnare un quadrato centrato nel mezzo del nostro canvas?
Iniziamo facendo uno pseudocodice che usi la dichiarazione `if` sul campo dello spazio. I principi per fare ciò sono straordinariamente simili a come immaginiamo lo scenario della carta millimetrata.
if ( (X più grande di 1) AND (Y più grande di 1) )
colora di bianco
else
colora di neroOra che abbiamo un’idea più chiara di come funziona, sostituiamo la dichiarazione `if` con `step()`, e invece di usare 10x10, usiamo i valori normalizzati tra 0.0 e 1.0.
uniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// per ogni valore restituirà 1.0 (bianco) o 0.0 (nero).
float left = step(0.1,st.x); // Simile a ( X più grande di 0.1 )
float bottom = step(0.1,st.y); // Simile a ( Y più grande di 0.1 )
// La moltiplicazione di left*bottom sarà simile alla porta logica AND.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
La funzione `step()` trasformerà ogni pixel al di sotto di 0.1 in nero (`vec3(0.0)`) e il resto in bianco (`vec3(1.0)`). La moltiplicazione tra left e bottom funziona come l’operazione logica AND, dove entrambi devono essere tra 1.0 per tornare a 1.0. Questo traccia due linee nere, una alla base e l’altra sul lato sinistro del canvas.
Nel codice precedente abbiamo ripetuto la struttura per ciascun asse (sinistra e base). Possiamo risparmiare alcune linee di codice passando due valori, invece di uno, direttamente a `step()` Ecco com’è:
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;Fino ad ora abbiamo disegnato solamente due bordi (base-sinistra) del nostro rettangolo. Facciamo gli altri due (alto-destra). Osserva il seguente codice:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Togliete il commento alle righe 21-22 e osservate come invertiamo le coordinate `st` e ripetiamo la stessa funzione `step()`. In questo modo il `vec2(0.0,0.0)` sarà nell’angolo in alto a destra. Questo è l’equivalente digitale del capovolgimento della pagina e della ripetizione della procedura precedente.
Osservate che alla riga 18 e alla riga 22 ogni lato è stato moltiplicato insieme. Questo equivale a scrivere:
vec2 bl = step(vec2(0.1),st); // sotto-sinistra
vec2 tr = step(vec2(0.1),1.0-st); // sopra-destra
color = vec3(bl.x bl.y tr.x * tr.y);Interessante, vero? Questa tecnica si basa tutta sull’uso di `step()`, della moltiplicazione per le operazioni logiche e il capovolgimento delle coordinate.
Prima di proseguire, provate i seguenti esercizi:
* Cambiate le misure e le proporzioni del rettangolo.
* Provate con lo stesso codice ma usando `smoothstep()` al posto di `step()`. Notate che cambiando valori, potete passare da margini indistinti a lati eleganti e lisci.
* Fate un’altra implementazione che usi `floor()`.
* Scegliete l’implementazione che preferite e create una sua funzione che potrete riusare in futuro. Rendete la vostra funzione flessibile ed efficiente.
* Create un’altra funzione che semplicemente tracci il contorno di un rettangolo.
* Come pensate di poter importare e spostare diversi rettangoli sullo stesso canvas? Se immaginate come, dimostrate le vostre capacità costruendo una composizione di rettangoli e colori che ricordi il dipinto di Piet Mondrian.
Circonferenze
Disegnare quadrati e rettangoli su carta millimetrata con coordinate cartesiane è semplice, ma le circonferenze richiedono un approccio diverso, specialmente per il fatto che abbiamo bisogno di un algoritmo “per-pixel”. Una soluzione è riassegnare le coordinate spaziali, in modo tale da poter usare la funzione `step()` per disegnare una circonferenza.
Come? Iniziamo tornando indietro alla lezione di matematica e al foglio a quadretti, sul quale abbiamo aperto il compasso con il raggio della circonferenza, abbiamo premuto l’ago del compasso al centro della circonferenza e tracciato il perimetro della circonferenza con una semplice rotazione.
Tradurre ciò in un shader, dove ogni quadrato sul foglio a quadretti è un pixel, implica chiedere ad ogni pixel (o thread) se si trova all’interno dell’area della circonferenza. Lo facciamo calcolando la distanza tra il pixel e il centro della circonferenza.
Esistono diversi modi per calcolare questa distanza. Il più semplice utilizza la funzione `distance()`, che al suo interno calcola la funzione `length()` della differenza tra due punti ( nel nostro caso le coordinate del pixel e il centro del canvas). La funzione `length()` non è altro che una scorciatoia dell’equazione dell’ipotenusa, che usa al suo interno la radice quadrata ((`sqrt()`)).
Potete usare `distance()`, `length()` o `sqrt()` per calcolare la distanza dal centro del canvas. Il codice seguente contiene queste tre funzioni e il fatto non sorprendente è che ciascuna restituisce esattamente lo stesso risultato.
* Commentate e togliete il commento alle righe per provare i diversi modi di ottenere il medesimo risultato.
<div class="codeAndCanvas" data="circle-making.frag"></div>
Nell’esempio precedente abbiamo mappato la distanza dal centro del canvas con la luminosità del colore del pixel. Più un pixel è vicino al centro, minore ( più scuro) è il suo valore. Notate che i valori non diventano troppo alti perché la massima distanza dal centro ( `vec2(0.5, 0.5)` ) va a malapena oltre 0.5. Osservate attentamente questa mappa e pensate:
* Cosa potete dedurre da essa?
* Come possiamo usarla per tracciare una circonferenza?
* Modificate il codice qui sopra per far sì che l’intero gradiente circolare sia contenuto nel canvas.
Campo di distanza
Possiamo anche immaginare l’esempio soprastante come una carta delle altitudini, dove più scuro implica più alto. Il gradiente ci mostra qualcosa di simile al modello creato da un cono. Immaginate di trovarvi sulla cima di un cono. La distanza orizzontale dal bordo del cono è 0.5. Questa sarà costante in ogni direzione. Scegliendo dove “tagliare” il cono, otterrete una superficie circolare più grande o più piccola.
In sostanza, per creare delle forme, stiamo utilizzando una reinterpretazione dello spazio basata sulla distanza dal centro. Questa tecnica è conosciuta come “campo di distanza” (distance field) ed è usata in modi diversi, dai profili dei caratteri (font) alla grafica 3D.
Esercitatevi con i seguenti esercizi:
* Usate `step()` per trasformare in bianco tutto ciò che è al di sopra di 0.5 e al di sotto di 0.0.
* Invertite i colori dello sfondo e del primo piano.
* Usate `smoothstep()`, provate con valori diversi per ottenere dei margini lisci sulla vostra circonferenza.
* Una volta che siete soddisfatti con un’implementazione, createne una funzione, che potrete riutilizzare in futuro.
* Aggiungete il colore alla circonferenza.
* Riuscite a creare animazioni sulla circonferenza per farla crescere e rimpicciolire, simulando il battito del cuore? (Potete trarre ispirazione dalle animazioni nel capitolo precedente).
* Che ne dite di spostare questa circonferenza? Riuscite a spostarla e a mettere altre circonferenze su uno stesso canvas?
* Cosa succede se combinate i campi di distanza con diverse operazioni e funzioni?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Create tre composizioni usando questa tecnica. Se poi riuscite ad animarle, ancora meglio!
#### Per la vostra cassetta degli attrezzi
In termini di potenza computazionale, la funzione `sqrt()`, e le altre funzioni che dipendono da essa, possono essere dispendiose. Ecco un altro modo per creare un campo di distanza circolare, basato sulla funzione `dot()`.
<div class="codeAndCanvas" data="circle.frag"></div>
Proprietà utili dei campi di distanza
I campi di distanza possono essere usati per disegnare quasi qualsiasi cosa. Ovviamente, più una forma è complessa, più sarà complicata la sua equazione, ma una volta che avete la formula per creare un campo di distanza di una certa forma è molto facile combinare e/o applicare degli effetti ad essa, come lati lisci e profili multipli. Per questo motivo, i campi di distanza hanno molto successo nella rappresentazione dei caratteri (font), come Mapbox GL Labels, Matt DesLauriers Material Design Fonts e come è descritto nel Capitolo 7 del libro "iPhone 3D Programming", O’Reilly
Osservate il codice seguente.
<div class="codeAndCanvas" data="rect-df.frag"></div>
Iniziamo spostando il sistema di coordinate al centro e riducendolo a metà per riassegnare i valori di posizione tra -1 e 1. Alla riga 24 stiamo vedendo i valori del campo di distanza usando una funzione `fract()`, semplificando la visualizzazione dei motivi che essi creano. Il motivo del campo di distanza si continua a ripetere come i cerchi in un giardino zen.
Osserviamo la formula del campo di distanza alla riga 19. Lì stiamo calcolando la distanza dalla posizione su `(.3,.3)` o `vec3(.3)` in tutti e quattro i quadranti ( questo è ciò che fa `abs()` qui) .
Se togliete il commento alla riga 20, noterete che stiamo collegando le distanze a questi quattro punti usando il `min()` a zero. Il risultato produce un nuovo motivo interessante.
Ora provate a togliere il commento alla riga 21; stiamo facendo la stessa cosa ma usando la funzione `max()`. Il risultato è un rettangolo con gli angoli arrotondati. Notate come gli anelli del campo di distanza diventano più lisci mano a mano che si allontanano dal centro.
Terminate togliendo il commento una ad una alle righe da 27 a 29, per capire i diversi usi dei motivi di un campo di distanza.
Forme polari
Nel capitolo sul colore, abbiamo assegnato alle coordinate cartesiane le coordinate polari, calcolando il raggio e gli angoli di ciascun pixel con la seguente formula:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);Usiamo parte di questa formula all’inizio del capitolo per tracciare una circonferenza. Abbiamo calcolato la distanza dal centro usando `length()`. Ora che conosciamo i campi di distanza, possiamo imparare un altro modo per disegnare le forme usando le coordinate polari.
Questa tecnica è un po’ restrittiva ma molto semplice. Consiste nel cambiare il raggio di una circonferenza in funzione dell’angolo per realizzare forme diverse. Come funziona la modulazione? Esatto, usando le funzioni di forma!
Qui sotto troverete le medesime funzioni sul diagramma cartesiano e in un esempio shader nelle coordinate polari ( tra le righe 21 e 25). Togliete il commento alle funzioni una alla volta, facendo attenzione alla relazione tra un sistema di coordinate e l’altro.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Provate a:
* Animare queste forme.
Combinare diverse funzioni di forma per fare dei buchi* nella forma e creare fiori, fiocchi di neve e degli ingranaggi.
Usare la funzione `plot()` che abbiamo usato nel capitolo sulle funzioni di forma*, per disegnare solamente la sagoma.
Unire i poteri
Ora che abbiamo imparato come modulare il raggio di una circonferenza in relazione all’angolo, usando la funzione `atan()` per disegnare diverse forme, possiamo imparare come usare `atan()` con i campi di distanza e applicare tutti i trucchi e gli effetti possibili con i campi di distanza.
Il nostro trucco userà il numero di lati di un poligono per costruire il campo di distanza, usando le coordinate polari. Controllate il seguente codice di Andrew Baldwin.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Usando quest’esempio, create una funzione che inserisca la posizione e il numero degli angoli di una forma desiderata e restituisca il valore di un campo di distanza.
* Mescolate i campi di distanza usando `min()` e `max()`.
* Scegliete un logo geometrico da riprodurre usando i campi di distanza.
Congratulazioni! Avete affrontato la parte più complicata! Fate una pausa per poter assimilare questi concetti: disegnare delle semplici forme con Processing è facile, ma qui no. Nella “terra degli Shader”, disegnare le forme è contorto e può essere faticoso adattarsi al nuovo paradigma di codificazione.
Alla fine di questo capitolo troverai un link a [PixelSpirit Deck] (https://patriciogonzalezvivo.github.io/PixelSpiritDeck/) questo mazzo di carte ti aiuterà ad apprendere nuove funzioni SDF, compilarle nei tuoi progetti e utilizzarle sui tuoi shader. Il mazzo ha una curva di apprendimento pregresiva, quindi prendere una carta al giorno e lavorarci su spingerà e metterà alla prova le tue abilità per mesi.
Ora che sapete come disegnare le forme, sono sicuro che vi verranno in mente nuove idee. Nel capitolo successivo imparerete a spostare, ruotare e ridimensionare le forme. Questo vi permetterà di fare delle composizioni!
---
07/README Pl (07/README-pl.md)
Kształty
Nareszcie! Czekaliśmy na ten moment! Poznałeś większość podstaw GLSL, jego typów i funkcji. Ćwiczyłeś również wykorzystanie shaping functions. Teraz nadszedł czas, aby połączyć to wszystko w całość. Jesteś w stanie sprostać temu wyzwaniu! W tym rozdziale dowiesz się, jak procedrualnie i równolegle rysować proste kształty.
Prostokąt
Wyobraźmy sobie, że mamy papier w kratkę, taki jaki używaliśmy na lekcjach matematyki i naszym zadaniem domowym jest narysowanie kwadratu. Kartka papieru jest w wymiarach 10x10, a kwadrat w 8x8. Co zrobisz?
Zamalowałbyś wszystko poza pierwszym i ostatnim rzędem oraz pierwszą i ostatnią kolumną, tak?
Jak to się ma do shaderów? Każdy mały kwadracik naszej kartki papieru to wątek (piksel). Każdy mały kwadrat zna swoje położenie, podobne do współrzędnych na szachownicy. W poprzednich rozdziałach zmapowaliśmy x i y na kanały kolorów czerwony i zielony oraz nauczyliśmy się korzystać z ciasnego dwuwymiarowego terytorium pomiędzy 0.0 a 1.0. Jak możemy tę wiedzę wykorzystać, aby narysować wyśrodkowany kwadrat na środku naszej kanwy?
Zacznijmy od naszkicowania pseudokodu, który używa ifów na współrzędnych kanwy. Idea jest podobna jak w przypadku z papieram w kratke.
if ( (X WIĘKSZE NIŻ 1) ORAZ (Y WIĘKSZE NIŻ 1) )
pomaluj na biało
else
pomaluj na czarnoTeraz, gdy mamy lepsze wyobrażenie o tym, jak to będzie działać, zastąpimy ifa funkcją step(), a zamiast używać siatki 10x10 użyjmy znormalizowanych odpowiedników pomiędzy 0.0 a 1.0:
uniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Obie linijki zwracają 1.0 (biały) lub 0.0 (czarny).
float left = step(0.1,st.x); // Równoważnie: (X WIĘKSZE NIŻ 0.1)
float bottom = step(0.1,st.y); // Równoważnie: (Y WIĘKSZE NIŻ 0.1)
// Mnożenie left*bottom jest podobne do logicznego AND.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
Funkcja step() "pomaluje" każdy piksel poniżej 0.1 na czarno (vec3(0.0)) a resztę na biało (vec3(1.0)). Mnożenie pomiędzy left i bottom działa jak logiczna operacja AND, gdzie obie muszą być 1.0 aby zwrócić 1.0, a gdy przynajmniej jedna jest 0.0, to obie są 0.0. W efekcie otrzymujemy dwie czarne linie, jedną na dole, a drugą po lewej stronie kanwy.
W poprzednim kodzie powtarzamy funkcję step() dla każdej osi (lewej i dolnej). Możemy zaoszczędzić kilka linii kodu, przekazując obie wartości razem zamiast pojedynczo. Wygląda to następująco:
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;Do tej pory narysowaliśmy tylko dwie krawędzie (dolna-lewa) naszego prostokąta. Dorysujmy teraz dwie pozostałe (górna-prawa). Sprawdź następujący kod:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Odkomentuj linijki 21-22 i zobacz jak odwracamy współrzędne st - w ten sposób vec2(0,0,0) znajdzie się w prawym górnym rogu. Otrzymaliśmy odbicie lustrzane. Teraz wystarczy przekazać te odwrócone współrzędne do step().
Zwróć uwagę, że w linijkach 18 i 22 wszystkie boki są mnożone przez siebie. Jest to równoznaczne z napisaniem:
vec2 bl = step(vec2(0.1),st); // bottom-left
vec2 tr = step(vec2(0.1),1.0-st); // top-right
color = vec3(bl.x bl.y tr.x * tr.y);Ciekawe, prawda? W tej technice chodzi o wykorzystanie step(), odwracania (ang. "flip") współrzędnych oraz mnożenia (jako operację logiczną AND).
Zanim przejdziesz dalej, spróbuj wykonać następujące ćwiczenia:
* Zmień rozmiar i proporcje prostokąta.
* Użyj smoothstep() zamiast step(). Zauważ, że zmieniając wartości, możesz przejść od rozmytych krawędzi do eleganckich gładkich granic.
* Zaimplementuj to samo, ale za pomocą floor() (+ mnożenie i dzielenie).
* Wybierz implementację, którą najbardziej lubisz i zrób z niej funkcję, którą możesz ponownie wykorzystać w przyszłości. Spraw, aby twoja funkcja była elastyczna i wydajna.
* Zrób inną funkcję, która po prostu rysuje kontur prostokąta.
* Jak myślisz, jak można narysować różne prostokąty na tej samej kanwie? Jeśli wymyślisz jak, pochwal się swoimi umiejętnościami, tworząc kompozycję z prostokątów i kolorów, która przypomina obraz Pieta Mondriana.
Koła
Łatwo jest rysować kwadraty na papierze w kratkę i prostokąty na współrzędnych kartezjańskich, ale okręgi wymagają innego podejścia, zwłaszcza że potrzebujemy algorytmu działającego na każdym pikselu z osobna. Jednym z rozwiązań jest zmapowanie współrzędnych tak, abyśmy mogli użyć funkcji step() do narysowania okręgu.
Jak to zrobić? Przypomnijmy sobie lekcje matematyki, gdzie rozpościeraliśmy ramiona cyrkla na długość promienia okręgu, wciskaliśmy jedno ramię cyrkla w środek okręgu, a następnie obrysowywaliśmy krawędź okręgu obracając drugie ramię.
Przełożenie tego na shader, w którym każdy piksel jast jak kratka na papierze, implikuje zadawanie każdemu pikselowi (wątkowi) pytania, czy znajduje się wewnątrz obszaru koła. Robimy to poprzez obliczenie odległości od danego piksela do środka okręgu.
Istnieje kilka sposobów na obliczenie tej odległości. Najprostszy z nich wykorzystuje funkcję distance(), która oblicza length() różnicy pomiędzy dwoma punktami (w naszym przypadku współrzędną piksela i środkiem kanwy). Funkcja length() to nic innego jak przekształcone twierdzenie Pitagorasa:
Możesz użyć distance(), length() lub sqrt() aby obliczyć odległość do centrum kanwy. Poniższy kod zawiera te trzy funkcje i nie zaskakuje fakt, że każda z nich zwraca dokładnie taki sam wynik.
Zakomentuj i odkomentuj linijki, aby wypróbować różne sposoby uzyskania tego samego wyniku.
<div class="codeAndCanvas" data="circle-making.frag"></div>
W powyższym przykładzie mapujemy odległość do centrum kanwy na jasność piksela. Im bliżej centrum znajduje się piksel, tym niższą (ciemniejszą) ma wartość. Zauważ, że wartości nie są zbyt wysokie, ponieważ od centrum ( vec2(0.5, 0.5) ) maksymalna odległość ledwo przekracza 0.5. Pokontempluj nad dokonanym mapowaniem i pomyśl:
* Co można z niego wywnioskować?
* Jak możemy je użyć do narysowania koła?
* Zmodyfikuj powyższy kod, aby zawrzeć cały gradient wewnątrz kanwy. (Wskazówka: użyj mnożenia)
Pole odległości
Możemy również myśleć o powyższym przykładzie jako o mapie wysokości, gdzie im ciemniej tym wyżej. Gradient pokazuje nam coś podobnego do wzoru tworzonego przez stożek. Wyobraź sobie, że jesteś na szczycie tego stożka. Pozioma odległość do krawędzi stożka wynosi 0.5. Będzie ona stała we wszystkich kierunkach. Wybierając miejsce "przecięcia" stożka otrzymamy większą lub mniejszą powierzchnię kołową.
Zasadniczo używamy reinterpretacji przestrzeni (w oparciu o odległość do centrum), aby tworzyć kształty. Ta technika jest znana jako "pole odległości" (ang "distance field") i jest używana na różne sposoby, od konturów czcionek do grafiki 3D.
Spróbuj następujących ćwiczeń:
* Użyj step(), aby zamienić wszystko powyżej 0.5 na białe, a wszystko poniżej na czarne.
* Odwróć kolory tła i pierwszego planu.
* Używając smoothstep(), poeksperymentuj z różnymi wartościami, aby uzyskać ładne, gładkie granice na swoim okręgu.
* Gdy już będziesz zadowolony ze swojej implementacji, stwórz z niej funkcję, którą będziesz mógł ponownie wykorzystać w przyszłości.
* Dodaj kolor do koła.
* Czy możesz zanimować swój krąg, aby rosnął i kurczył się, symulując bijące serce? (Możesz zaczerpnąć inspirację z animacji w poprzednim rozdziale).
* A co z przesuwaniem tego okręgu? Czy możesz go przesuwać i umieszczać różne okręgi na jednej kanwie?
* Co się stanie, jeśli połączysz pola odległości razem, używając różnych funkcji i operacji?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Zrób trzy kompozycje z wykorzystaniem tej techniki. Jeśli są one animowane, jeszcze lepiej!
#### Twoja skrzynka z narzędziami
Pod względem mocy obliczeniowej funkcja sqrt() - i wszystkie funkcje, które od niej zależą - mogą być kosztowne. Oto inny sposób tworzenia okrągłego pola odległości za pomocą produktu skalarnego dot():
<div class="codeAndCanvas" data="circle.frag"></div>
Przydatne własności pól odległości
Pola odległości mogą być używane do rysowania prawie wszystkiego. Oczywiście im bardziej złożony jest kształt, tym bardziej skomplikowane będzie jego równanie, ale gdy już masz formułę do tworzenia pól odległości danego kształtu, bardzo łatwo jest połączyć i/lub zastosować do niego efekty, takie jak gładkie krawędzie i wiele konturów. Z tego powodu, pola odległości są popularne w renderowaniu czcionek, takich jak Mapbox GL Labels, Matt DesLauriers Material Design Fonts i jak to jest opisane w rozdziale 7 iPhone 3D Programming, O'Reilly.
Przyjrzyj się następującemu kodowi.
<div class="codeAndCanvas" data="rect-df.frag"></div>
Zaczynamy od przeniesienia układu współrzędnych na środek i skurczenia go o połowę, mapując wartości pozycji pomiędzy -1 a 1. W linijce 24 wizualizujemy wartości pola odległości za pomocą funkcji fract() ułatwiając dostrzeżenie tworzonego przez nie wzoru. Wzór pola odległości powtarza się jak pierścienie w ogrodzie Zen.
Przyjrzyjmy się wzorowi pola odległości w linijce 19. Obliczamy tam odległość do współrzędnej (.3,.3) lub vec3(.3) we wszystkich czterech kwadrantach (właśnie po to jest tam abs()).
Jeśli odkomentujesz linijkę 20, zauważysz, że łączymy odległości do tych czterech punktów za pomocą min() do zera. W rezultacie otrzymujemy nowy interesujący wzór.
Spróbuj teraz odkomentować linijkę 21; robimy to samo, ale używamy funkcji max(). Rezultatem jest prostokąt z zaokrąglonymi rogami. Zauważ, jak pierścienie pola odległości stają się gładsze, im bardziej oddalają się od środka.
Dokończ odkomentowywanie linijek 27 do 29 jedna po drugiej, aby zrozumieć różne zastosowania pól odległości.
Krzywe biegunowe
W rozdziale o kolorze mapujemy współrzędne kartezjańskie na współrzędne biegunowe, obliczając promień i kąt każdego piksela za pomocą następującego wzoru:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);Część tego wzoru wykorzystaliśmy na początku rozdziału do narysowania okręgu. Odległość do środka obliczyliśmy za pomocą length()
. Teraz, gdy wiemy już o polach odległości, możemy poznać inny sposób rysowania kształtów za pomocą współrzędnych biegunowych.Technika ta jest nieco restrykcyjna, ale bardzo prosta. Polega ona na zmianie promienia okręgu w zależności od kąta, aby uzyskać różne kształty. Jak dokonuje się ta zmiana? Z użyciem shaping functions!
Poniżej znajdziesz dwa interaktywne przykłady, w których te same funkcje występują we współrzędnych kartezjańskich i w biegunowych (pomiędzy linijkami 21 i 25). Odkomentuj te funkcje jedna za drugą, zwracając uwagę na zależności między jednym układem współrzędnych a drugim.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Spróbuj:
* Zanimować te kształty
Połącz różne shaping functions by zrobić dziury* w kształtach, aby powstały kwiaty, płatki śniegu i zębatki.
Użyj funkcji plot() z rodziału Shaping functions* i narysuj sam kontur (bez wypełnienia)
Łącząc siły
Teraz gdy wiemy, jak zmieniać promień koła w zależności od kąta z użyciem atan(), możemy spróbować połączyć atan() z polami odległości.
Trik polega na wykorzystaniu liczby krawędzi wielokąta, by skonstruować pole odległości z użyciem współrzędnych polarnych. Sprawdź następujący kod od Andrew Baldwin.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Korzystając z tego przykładu, stwórz funkcję, która przyjmuje położenie i liczbę kątów pożądanego wielokąta, a zwraca wartość pola odległości.
* Połącz pola odległości ze sobą z użyciem min() i max().
* Zreplikuj dowolne logo z użyciem pól odległości
Gratulacje! Udało ci się przebrnąć przez bardzo trudny materiał! Choć rysowanie prostych kształtów w Processing jest proste, to tutaj już nie. W świecie shaderów rysowanie kształtów jest zawiłe; przestawienie się na ten nowy sposób programowania może być męczące.
Na dole strony znajdziesz link do PixelSpirit Deck. Jest to talia kart, która pomoże ci nauczyć się nowych funkcji SDF (ang. "Signed distance field" - pole odległości z uwzględnieniem wartości ujemnych), które wykorzystasz w swoich pracach i shaderach. Poziom trudności jest progresywny, więc praca nad jedną kartą dziennie zapewni ci wyzwania na kolejne miesiące.
Wiedząc jak rysować kształty, z pewnością przyjdą ci do głowy nowe pomysły. W następnym rozdziale nauczysz się przesuwać, obracać i skalować kształty. Pozwoli ci to tworzyć kompozycje!
---
07/README Pt (07/README-pt.md)
Formas
Finalmente! A gente vem construindo habilidades para este momento! Você aprendeu a maioria das fundações, tipo s funções GLSL. Você praticou suas equações de formas várias vezes. Agora é a hora de juntar tudo. Você está pronto para este desafio! Neste capítulo, vai aprender como desenhar formas simples, de forma procedural paralela.
Retângulo
Imagine que tenhamos um papel quadriculado como os que usamos nas aulas e exercícios de casa de matemática, e queremos desenhar um quadrado. O tamanho do papel é 10x10 e o quadrado deveria ser 8x8. O que você vai fazer?
Você pintaria tudo, menos a primeira linha e a última, e a primeira coluna e a última, certo?
E como isso se relaciona com os shaders? Cada quadradinho de nosso papel é uma thread (um pixel). Cada quadradinho sabe sua posição, como as coordenadas de um tabuleiro de xadrez. Em capítulos anteriores, nós mapeamos x e y para os canais de cores vermelho e verde, e aprendemos como usar o estreito território bidimensional entre 0.0 e 1.0. Como podemos usar isso para desenhar um quadrado centralizado no meio da nossa tela?
Vamos começar rascunhando um pseudocódigo que usa vários if no campo espacial. Os princípios para fazer isso são notavelmente similares ao modo que pensamos no cenário do papel quadriculado.
if ( (X MAIOR QUE 1) AND (Y MAIOR QUE 1) )
pinta de branco
else
pinta de pretoAgora que temos uma ideia melhor de como isos vai funcionar, vamos substituir os if com step(), e, em vez de usar 10x10, vamos usar valores normalizados entre 0.0 e 1.0:
uniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Cada resultado retorna 1.0 (branco) ou 0.0 (preto).
float left = step(0.1,st.x); // Similar a ( X maior que 0.1 )
float bottom = step(0.1,st.y); // Similar a ( Y maior que 0.1 )
// A multiplicação left*bottom vai ser similar ao AND lógico.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
A função step() vai tornar cada pixel abaixo de 0.1 preto (vec3(0.0)) e o resto branco (vec3(1.0)) . A multiplicação entre left e bottom funciona como um AND lógico, onde os dois devem estar 1.0 para retornar 1.0. Isso desenha duas linhas pretas, uma no fundo, e outra na lado esquerdo do canvas.
No código anterior, repetimos a estrutura para cada eixo (esquerdo e fundo). Podemos economizar algumas linhas de código, passando 2 vaores diretamente para step() ao invés de apenas um. Seria algo assim:
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;Até agora, só desenhamos duas bordar (fundo-esquerda) de nosso retângulo. Vamos desenhar as outras duas (topo-direita). Veja esse código:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Descomente as linhas 21-22 e veja como invertemos as coordenadas st e repetimos a mesma função step(). Desse jeito, o vec2(0.0,0.0) vai estar no canto superior direito. Isso é o equivalente digital a girar a página e repetir o mesmo procedimento.
Note que nas linhas 18 e 22, todos os lados estão sendo multiplicados juntos. É o mesmo que escrever:
vec2 bl = step(vec2(0.1),st); // bottom-left
vec2 tr = step(vec2(0.1),1.0-st); // top-right
color = vec3(bl.x bl.y tr.x * tr.y);Interessante, certo? Essa técnica é, basicamente, usar step() e multiplicação para operações lógicas, e flipar as coordenadas.
Antes de prosseguir, tente os exercícios a seguir:
* Mude o tamanho e as proporções do retângulo.
* Experimente com o mesmo código, mas usando smoothstep() no lugar de step(). Note que, ao mudar os valores, você pode ir de beiradas embaçadas até elegantes bordas suaves.
* Faça uma outra implementação que use floor().
* Escolha a implementação de que gostar mais, e faça uma função dela, que você possa reutilizar no futuro. Deixe sua função flexível e eficiente.
* Faça outra função que apenas desenhe o contorno do retângulo.
* Como você acha que poderia mover e colocar retângulos diferentes na mesma tela? Se descobrir como, mostre suas habilidades fazendo uma composição de retângulos e cores que se pareça com uma pintura de Piet Mondrian.
Círculos
É fácil desenhar quadrados em um papel quadriculado, e retângulos em coordenadas cartesianas, mas círculos requerem outra abordagem, especialmente porque precisamos de um algoritmo "por pixel". Uma solução é remapear as coordenadas espaciais de modo que possamos usar uma função step() para desenhar um círculo.
Como? Vamos começar, voltando um pouco às aulas de matemática e o papel quadriculado, onde nós abríamos um compasso até o raio do círculo, apertamos uma das pontas do compasso no centro do círculo, e então tracejamos o contorno do círculo com uma rodada do compasso.
Traduzindo isso para um shader, onde cada quadrado no papel é um pixel, implica em perguntar a cada pixel (ou thread) se ele está dentro da área do círculo. Fazemos isso calculando a distância do pixel até o centro do círculo.
Existem várias formas de se calcular essa distância. A mais fácil usa a função distance(), que, internamente, calcula o length() (comprimento) da diferença entre dois pontos (em nosso caso, a coordenada do pixel, e o centro da tela). A função length() nada mais é que um atalho para a equação de hipotenusa que usa raiz quadrada (sqrt()) internamente.
Você pode usar distance(), length() ou sqrt() para calcular a distância até o centro da tela. O código a seguir contém essas três funções e o fato não-surpreendente de que retornam o mesmo resultado.
* Comente e descomente as linhas para testar as diferentes formar de obter o mesmo resultado.
<div class="codeAndCanvas" data="circle-making.frag"></div>
No exemplo anterior, nós mapeamos a distância até o centro para o brilho da cor de cada pixel. Quanto mais ao centro está um pixel, mais escuro ele é (menor valor de cor). Note que os valores não ficam muito altos porque a partir do centro ( vec2(0.5, 0.5) ) a distância máxima mal passa de 0.5. Contemple esse mapa e pense:
* O que você pode inferir disso?
* Como podemos usar isso para desen um círculo?
* Modifique o código acima, de modo a conter o gradiente circular inteiro dentro do canvas.
Campo de distância
Também podemos pensar no exemplo acima como um mapa de altitude, onde áreas mais escuras implicam em maior altura. O gradiente nos mostra algo similar ao padrão feito por um cone. Imagine-se no topo desse cone. A distância horizontal até a borda do cone é 0.5. Isso será constante em todas as direções. Escolhendo onde "cortar" o cone, vai te dar uma superfície circular maior ou menor.
Basicamente, estamos usando uma reinterpretação do espaço (baseado na distância até o centro) para fazer formas. Essa técnica é conhecida como "campo de distância" e é usada e diferentes modos, desde outlines de fontes até gráficos 3D.
Tente os seguintes exercícios:
* Use o step() para tornar tudo acima de 0.5 em branco e tudo abaixo para 0.0.
* Inverta as cores do fundo e do primeiro plano.
* Usando smoothstep(), faça experiências com valores diferentes para conseguir uma borda suave no seu círculo.
* Uma vez que esteja feliz com uma implementação, faça uma função com ela, para reutilizar no futuro.
* Adicione cor ao círculo.
* Você pode animar seu círculo, para crescer e encolher, simulando a batida de um coração? (Você pode ter alguma inspiração, da animação do capítulo anterior)
* Que tal mover esse círculo? Você consegue movê-lo para lugares diferentes na tela?
* O que acontece se você combinar campos de distância usando diferentes funções e operações?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Faça três composições usando essa técnica. Se forem animadas, melhor ainda!
#### Para sua caixa de ferramentas
Em termos de poder computacional, a função sqrt() - e todas as que dependem dela - pode ser bem cara. Aqui vai outra forma de criar um campo de distância usando dot() (produto escalar).
<div class="codeAndCanvas" data="circle.frag"></div>
Propriedades úteis de um Campo de Distância
Campos de Distância podem ser usados para desenhar quase tudo. Obviamente, quanto mais complexa for uma forma, mais complicada vai ser a equação, mas uma vez que você tenha a fórmula para fazer um campo de distância de uma forma específica, é muito fácil combinar e/ou aplicar efeitos a ela, como bordas suaves, e múltiplos outlines. Por causa disso, os campos de distância são populares para rendereizar fontes, como Mapbox GL Labels, Matt DesLauriers Material Design Fonts e como descrito no capítulo 7 do livro iPhone 3D Programming, O’Reilly.
Dê uma olhada no código a seguir.
<div class="codeAndCanvas" data="rect-df.frag"></div>
Começamos movendo o sistema de coordenadas para o centro e encolhendo-o pela metade, para remapear os valores de posição entre -1 e 1. Também, na linha 24, estamos visualizando os valores do campo de distância usando uma função fract() para facilitar que você veja o padrão que eles criam. O padrão do campo de distância se repete o tempo todo, como anéis num jardim Zen.
Vamos dar uma olhada na fórmula do campo, na linha 19. Ali, estamos calculando a distância até a posição (.3,.3) ou vec3(.3) em todos os nossos quatro quadrantes (é isso o que o abs() está fazendo aqui).
Se você descomentar a linha 20, vai notar que estamos combinando as distâncias para esses quatro pontos usando o min() para zero. O resultado produz um novo padrão bem interessante.
Agora, tente descomentar a linha 21; estamos fazendo o mesmo, mas usando a função max(). O rsultado é um retângulo com cantos arredondados. Note como os anéis do cmapo ditância ficam mais suaves quanto mais disntantes do centro.
Termine de descomentar as linhas 27 a 29 uma a uma, para entender os diferentes usos de um padrão de campo de distância.
Formas Polares
No capítulo sobre sores, nós mapeamos as coordenadas cartesianas para coordenadas polares, calculando o raio e os ângulos de cada pixel com essa fórmula:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);Usamos parte desta fórmula no começo do capítulo para desenhar um círculo. Nós calculamos a distância até o centro usando length(). Agora que sabemos sobre os os campos de distância, podemos aprender outra forma de desenhar formar, usando coordenadas polares.
Esta técnica é um pouco restritiva, mas bem simples. Ela consiste em mudar o raio de um círculo dependendo do ângulo, para obter formas diferentes. Como a modulação funciona? Sim, usando funções de formas!
Abaixo, você vai encontrar as mesmas funções do gráfico cartesiano e em um exemplo shader de coordenadas polares (entre as linhas 21 e 25). Descomente as funções uma a uma, prestando atenção na relação entre um sistema de coordenadas e o outro.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Tente:
* Animar essas formas.
Combinar funções de formas diferentes para cortar buracos* na forma, para fazer flores, flocos de neve e engrenagens.
Use a função plot() que usamos no capítulo sobre Funções de Formas* para desenhar só o contorno.
Combinando forças
Agora que aprendemos como modular o raio de um círculo de acordo com o ângulo, usando atan() para desenhar formas diferentes, podemos aprender a usar atan() com campos de distância e aplicar todos os truques e efeitos possíveis com campos de distância.
O truque vai usar o número de lados de um polígono para construir o campo de distância, usando coordenadas polares. Veja o seguinte código do Andrew Baldwin.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Usando este exemplo, faça uma função que tem como entrada a posição e o número de lados de uma forma desejada, e retorna um valor de campo de distância.
* Misture campos de distância usando min() e max().
* Escolha um logo geométrico para replicar, usando campos de distância.
Parabéns! Você já passou pela parte mais dura! Dê uma pausa e deixe os conceitos assentarem - desenhar formas simples no Processing é fácil, mas não aqui. Na terra dos shaders, desenhar formas é difícil, e pode ser cansativo se adaptar a esse novo paradigma de programação.
Agora que você já sabe como desenhar formas, tenho certeza de que novas ideias vão pular na sua mente. No capítulo a seguir, você vai aprender como mover, rotacionar e mudar a escala das formas. Isso vai te permitir a fazer composições!
---
07/README Ru (07/README-ru.md)
Фигуры
Наконец то! Мы долго тренировались, и вот этот момент настал! Вы изучили большую часть зяыка GLSL, его типы и функции. Вы раз за разом упражнялись с одномерными функциями, составляя их в сложные формулы. И мы готовы к очередному витку синтеза полученных знаний. Вперёд! В этой главе мы научимся процедурно рисовать простые геометрические фигуры с помощью параллельных вычислений.
Прямоугольник
Вообразите листок бумаги в клетку, как на уроке математики, где нам задали на дом нарисовать квадрат. Размер листа - 10х10, а квадрат должен быть 8х8. Справитесь?
Наверное, вы бы закрасили всё кроме верхней и нижней строки и кроме крайних столбцов, так?
Как это относится к шейдерам? Каждая клетка нашего листка - это один поток выполнения (то есть пиксель). Каждая клетка знает своё положение, будто это координаты на шахматной доске. В предыдущей главе мы отображали x и y в красный и зелёный цветовые каналы, и изучили как использовать узкий участок двумерной территории между 0.0 и 1.0. Как с помощью этого нарисовать квадрат в центре окна?
Начнём с псевдокода, использующего ветвление (if) над пространством. Принцип здесь тот же, что и в случае с бумагой в клеточку.
if ( (X GREATER THAN 1) AND (Y GREATER THAN 1) ) // если ((х больше 1) и (y больше 1))
paint white // красим в белый
else // иначе
paint black // красим в чёрныйТеперь, когда мы знаем что делать, давайте заменим if на функцию step(), а вместо листа 10х10 используем нормированные значения между 0.0 и 1.0.
uniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Каждый вызов вернёт 1.0 (белый) или 0.0 (чёрный).
float left = step(0.1,st.x); // То же, что ( X больше 0.1 )
float bottom = step(0.1,st.y); // То же, что ( Y больше 0.1 )
// Умножение left*bottom работает как логическое И.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
Функция step() окрасит пиксели ниже 0.1 в чёрный (vec3(0.0)), а остальные - в белый (vec3(1.0)). Умножение left на bottom работает как логическое И, где оба значения должны быть равны 1.0 чтобы вернуть 1.0. Этот код нарисует две чёрных линии: снизу и слева изображения.
В коде выше мы повторяем одно и то же действие для обеих осей. Мы можем сэкономить несколько строк кода, передав в step() два значения вместо одного:
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;Итак, мы нарисовали две границы прямоугольника: нижнюю и левую. Давайте дорисуем верхнюю и правую. Рассмотрим следующий код:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Раскомментируйте строки 21-22, чтобы обратить координаты st и повторить вызов step(). Теперь vec2(0.0,0.0) окажется в верхнем правом углу. Это переворот страницы с повторением предыдущей процедуры.
Заметим, что в строках 18 и 22 все стороны прямоугольника перемножаются. В более развёрнутом виде это выглядит так:
vec2 bl = step(vec2(0.1),st); // низ и лево
vec2 tr = step(vec2(0.1),1.0-st); // верх и право
color = vec3(bl.x bl.y tr.x * tr.y);Интересно? Вся эта техника направлена на использование step(), умножения в качестве логической операции и переворот координат.
Перед тем как продолжить, попробуйте выполнить упражнения:
* Измените размер и пропорции прямоугольника.
* Поэкпериментируйте с тем же кодом, используя smoothstep() вместо step(). Изменяя значения интервала перехода, можно варьировать вид границ от размыливания до элегантного сглаживания.
* Реализуйте то же самое с помощью функции floor().
* Выберете наиболее понравившуюся реализацию и сделайте из неё функцию, которую сможете использовать в будущем. Сделайте эту функцию гибкой и эффективной.
* Напишите функцию, которая рисует только контур прямоугольника.
* Каким образом вы будете рисовать несколько прямоугольников в одном окне и перемещать их? Если догадаетесь как это делается, попробуйте повторить композицию Пита Мондриана.
Круги
Очень просто рисовать квадраты на клетчатой бумаге и прямоугольники в декартовых координатах, но круги требуют иного подхода, особенно в наших попиксельных алгоритмах. Одно из решений - перейти к другим пространственным координатам, так чтобы можно было использовать функцию step() для рисования кругов.
Как? Давайте вернёмся к уроку математики и бумаге в клеточку. Раскроем циркуль на радиус круга, поставим иглу циркуля в центр и нарисуем окружность поворотом циркуля.
Переводя на язык шейдеров, где каждый пиксель подобен клетке на бумаге, мы должны будем спросить каждый пиксель (поток), лежит ли он внутри круга. Сделаем это, вычислив расстояние от пикселя до центра круга.
Есть несколько способов вычислить это расстояние. В простейшем случае используется функция distance(), внутри которой вычисляется длина (length()) разности между двумя точками (в нашем случае это пиксель и центр изображения). Функция length() - ни что иное, как короткое название для формулы длины гипотенузы, в которой используется квадратный корень (sqrt()).
Для вычисления расстояния от центра изображения можно использовать distance(), length() или sqrt(). Следующий код демонстрирует все три функции и тот неудивительный факт, что они возвращают один и тот же результат.
* Закомментируйте и раскомментируйте строки, чтобы попробовать различные способы достижения одного и того же.
<div class="codeAndCanvas" data="circle-making.frag"></div>
В предыдущем примере мы отображаем расстояние от центра изображения в яркость пикселя. Чем ближе пиксель к центру, тем он темнее. Заметим, что яркость не становится слишком большой, так как максимальное расстояние до центра (vec2(0.5, 0.5)) не превышает 0.5. Внимательно рассмотрите это отображение и подумайте:
* Что из него можно вывести?
* Как им пользоваться для рисования круга?
* Измените код, чтобы весь весь круговой градиент вошёл в изображение.
Поле расстояний
Пример выше можно рассматривать как карту высот, где темнее значит выше. Тогда круговой градиент является картой конуса. Представим, что мы стоим на вершине конуса. Расстояние до границы конуса по горизонтали равно 0.5, и оно одинаково во всех направлениях. Выбирая высоту разреза конуса горизонтальной плоскостью, мы можем получить окружность большего или меньшего размера.
Фактически, мы интерпретируем пространство в терминах расстояния до центра чтобы рисовать фигуры. Этот подход, известный как «поле расстояний», используется во многих областях, начиная от контуров шрифтов и заканчивая 3D-графикой.
Выполните следующие упражнения:
* Используйте step() чтобы превратить все что больше 0.5 в белый, а что меньше - в чёрный.
* Поменяйте местами цвета фона и фигуры.
* Используя smoothstep(), добейтесь красивой гладкой границы круга.
* Возьмите понравившуюся реализацию и смастерите функцию для использования в будущем.
* Добавьте кругу цвет.
* Сделайте анимации увеличения и уменьшения круга, похожую на сердцебиение. Подчерпните вдохновение из анимаций в предыдущей главе.
* Как насчёт перемещения круга? Попробуйте подвинуть его и разместить несколько кругов на одном рисунке.
* Что произойдёт, если скомбинировать поля расстояний используя несколько функций и операций?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Сделайте три композиции с помощью этой техники. Ещё лучше, если они будут движущимися!
#### Инструментарий
В терминах вычислительной мощности функция sqrt() и все зависимые от неё функции довольно затратны. Есть другой способ создания круглых полей расстояний с помощью скалярного произведения (dot()).
<div class="codeAndCanvas" data="circle.frag"></div>
Полезные свойства поля расстояний
С помощью поля расстояний можно нарисовать что угодно. Очевидно, чем сложнее фигура, тем сложнее будет уравнение. Но при наличии формул для создания поля расстояний определённой формы, вы можете с лёгкостью комбинировать фигуры и применять к ним различные эффекты: сглаживание границ, рисование нескольких контуров и т.п. Поэтому поля расстояний находят широкое применение в рисовании шрифтов, например Mapbox GL Labels, шрифты Material Design от Matt DesLauriers и 7 глава книги iPhone 3D Programming, издательство O’Reilly.
Рассмотрим следующий код:
<div class="codeAndCanvas" data="rect-df.frag"></div>
Начнём со сдвига системы координат в центр и сжатия её в 2 раза, чтобы отобразить координаты в пространство между -1.0 и 1.0. В строке 24 мы визуализируем поле расстояний с помощью fract(), чтобы было проще рассмотреть его рисунок. Рисунок поля расстояний повторяется снова и снова, как круги в дзенском саду.
Посмотрите на формулу поля расстояний в 19 строке. в ней мы рассчитываем расстояние до точки (0.3, 0.3) во всех четырёх квадрантах (именно для этого там нужен abs().
Раскомментируя строку 20, мы сравним эти расстояния с нулём используя функцию min(). В результате получится новый интересный рисунок.
Теперь раскомментируйте строку 21. Произойдёт то же самое, но с функцией max(), что даст на выходе прямоугольник со скругленными углами. Обратите внимание как кольца поля расстояний становятся более гладкими с удалением от центра.
Наконец, раскомментируйте строки 27 - 29 одну за другой и рассмотрите различные применения поля расстояний.
Фигуры в полярных координатах
В главе про цвет мы переходили от декартовых координат в полярным, вычисляя радиус и угол каждого пикселя с помощью формул:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);В начале главы мы использовали часть этих формул для рисования круга. Мы вычисляли расстояние до центра функцией length(). Теперь, вооружившись знаниями о полях расстояний, мы изучим другой способ рисования фигур в полярных координатах.
Этот подход немного ограничен, но очень прост. Мы будем изменять радиус окружности в зависимости от угла, чтобы получить различные фигуры. Как это работает? Конечно же, с помощью функций формы!
Ниже представлены одни и те же функции в декартовых и в полярных координатах (строки 21 - 25). Раскомментируйте функции одну за одной, обращая внимание на соотношение между системами координат.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Попробуйте:
* Привести графики в движение.
Комбинировать функции формы для вырезания отверстий* в фигурах, чтобы создать цветы, снежинки и шестерёнки.
Используйте plot() из главы о функциях формы* чтобы нарисовать только контур.
Сила комбинаций
Мы научились рисовать фигуры, манипулируя радиусом круга в зависимости от угла с помощью функции atan(). Теперь давайте попробуем использовать atan() совместно с полем расстояний чтобы применить его возможности к фигурам в полярных координатах.
В этом трюке для конструирования поля расстояний в полярных координатах используется количество граней многогранника. Изучите следующий код, написанный Эндрю Болдуином.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Используя этот пример, напишите функцию, которая принимает положение и количество углов многогранника и возвращает значение поля расстояний в точке.
* Смешайте поля расстояний с помощью функции min() и max().
* Найдите какой-нибудь логотип из геометрических фигур и воспроизведите его в виде поля расстояний.
Поздравляю! Мы прошли трудную тему! Вдохните и дайте изученным принципам устояться. Рисовать фигуры - это просто в Processing, но не здесь. В мире шейдеров алгоритмы рисования фигур очень заковыристы, и адаптация к такой парадигме программирования может стоить значительных усилий.
В конце этой главы вы найдете ссылку на [PixelSpirit Deck] (https://patriciogonzalezvivo.github.io/PixelSpiritDeck/), эта колода карт поможет вам изучить новые функции SDF, объединить их в свои проекты и использовать на ваших шейдерах. Колода имеет предварительную кривую обучения, поэтому, взяв одну карту в день и работая над ней, вы испытаете свои навыки на месяцы.
Теперь, когда вы умеете рисовать фигуры, новые идеи будут появляться в вашей голове с огромной скоростью. В следующей главе мы научимся двигать, вращать и масштабировать фигуры. Вы сможете делать композиции!
---
07/README Tr (07/README-tr.md)
Şekiller
Sonunda! Bu an için beceriler geliştiriyorduk! GLSL temellerinin, tiplerinin ve fonksiyonlarının çoğunu öğrendiniz. Şekillendirme denklemlerinizi tekrar tekrar pratik ettiniz. Şimdi hepsini bir araya koyma zamanı. Bu meydan okumaya hazırsınız! Bu bölümde basit şekilleri paralel prosedürel bir şekilde nasıl çizeceğinizi öğreneceksiniz.
Dikdörtgen
Matematik derslerinde kullandığımız gibi bir kareli kağıdımız olduğunu ve ödevimizin bir kare çizmek olduğunu hayal edin. Kağıt boyutu 10x10 ve karenin 8x8 olması gerekiyor. Ne yaparsınız?
İlk ve son satırlar ile ilk ve son sütun dışında her yeri boyarsınız, değil mi?
Bu shader'larla nasıl ilişkilidir? Kareli kağıdımızın her küçük karesi bir iş parçacığıdır (bir piksel). Her küçük kare, bir satranç tahtasının koordinatları gibi konumunu bilir. Önceki bölümlerde x ve y'yi kırmızı ve yeşil renk kanallarına eşledik ve 0.0 ile 1.0 arasındaki dar iki boyutlu bölgeyi nasıl kullanacağımızı öğrendik. Tuvalimizin ortasında ortalanmış bir kare çizmek için bunu nasıl kullanabiliriz?
Uzaysal alan üzerinde if ifadeleri kullanan sözde kod taslağı çizerek başlayalım. Bunu yapmanın prensipleri, kareli kağıt senaryosu hakkında düşünme şeklimize son derece benzer.
if ( (X GREATER THAN 1) AND (Y GREATER THAN 1) )
paint white
else
paint blackArtık bunun nasıl çalışacağı hakkında daha iyi bir fikrimiz olduğuna göre, if ifadesini step() ile değiştirelim ve 10x10 kullanmak yerine 0.0 ile 1.0 arasında normalize edilmiş değerler kullanalım:
uniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Her sonuç 1.0 (beyaz) veya 0.0 (siyah) döndürecektir.
float left = step(0.1,st.x); // ( X 0.1'den büyük ) ile benzer
float bottom = step(0.1,st.y); // ( Y 0.1'den büyük ) ile benzer
// left*bottom çarpımı mantıksal AND'e benzer olacaktır.
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
step() fonksiyonu 0.1'in altındaki her pikseli siyaha (vec3(0.0)) ve geri kalanını beyaza (vec3(1.0)) çevirecektir. left ile bottom arasındaki çarpma, mantıksal AND işlemi gibi çalışır; burada her ikisi de 1.0 olmalıdır ki 1.0 döndürülsün. Bu, tuvalin sol tarafında ve alt tarafında iki siyah çizgi çizer.
Önceki kodda her eksen (sol ve alt) için yapıyı tekrarlıyoruz. Bir yerine iki değeri doğrudan step()'e geçirerek bazı kod satırlarından tasarruf edebiliriz. Şöyle görünür:
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;Şu ana kadar dikdörtgenimizin sadece iki kenarını (sol-alt) çizdik. Diğer ikisini (sağ-üst) de yapalım. Aşağıdaki koda göz atın:
<div class="codeAndCanvas" data="rect-making.frag"></div>
st koordinatlarını nasıl tersine çevirdiğimizi ve aynı step() fonksiyonunu nasıl tekrarladığımızı görmek için 21-22. satırların yorumunu kaldırın. Bu şekilde vec2(0.0,0.0) sağ üst köşede olacaktır. Bu, sayfayı çevirmenin ve önceki prosedürü tekrarlamanın dijital karşılığıdır.
18 ve 22. satırlarda tüm kenarların birlikte çarpıldığını not edin. Bu, şunu yazmaya eşdeğerdir:
vec2 bl = step(vec2(0.1),st); // sol-alt
vec2 tr = step(vec2(0.1),1.0-st); // sağ-üst
color = vec3(bl.x bl.y tr.x * tr.y);İlginç değil mi? Bu teknik tamamen step() kullanma, çarpma ile mantıksal işlemler yapma ve koordinatları çevirme ile ilgilidir.
İlerlemeden önce, aşağıdaki alıştırmaları deneyin:
* Dikdörtgenin boyutunu ve oranlarını değiştirin.
* step() yerine smoothstep() kullanarak aynı kodu deneyin. Değerleri değiştirerek bulanık kenarlardan zarif düzgün sınırlara geçebileceğinizi not edin.
* floor() kullanan başka bir uygulama yapın.
* En çok beğendiğiniz uygulamayı seçin ve gelecekte yeniden kullanabileceğiniz bir fonksiyon yapın. Fonksiyonunuzu esnek ve verimli yapın.
* Sadece dikdörtgenin dış hatlarını çizen başka bir fonksiyon yapın.
* Aynı tuvalde farklı dikdörtgenleri nasıl hareket ettirip yerleştirebileceğinizi düşünüyor musunuz? Nasıl yapılacağını bulursanız, Piet Mondrian tablosuna benzeyen bir dikdörtgen ve renk kompozisyonu yaparak becerilerinizi sergileyin.
Daireler
Kareli kağıda kareler ve Kartezyen koordinatlarda dikdörtgenler çizmek kolaydır, ancak daireler farklı bir yaklaşım gerektirir, özellikle "piksel başına" bir algoritmaya ihtiyacımız olduğundan. Bir çözüm, bir daire çizmek için step() fonksiyonunu kullanabilmemiz amacıyla uzaysal koordinatları yeniden eşlemektir.
Nasıl mı? Matematik dersine ve kareli kağıda geri dönerek başlayalım; burada pergeli dairenin yarıçapına açtık, pergelin bir ucunu dairenin merkezine bastırdık ve sonra basit bir dönüşle dairenin kenarını çizdik.
Bunu, kareli kağıdın her karesinin bir piksel olduğu bir shader'a çevirmek, her piksele (veya iş parçacığına) dairenin alanının içinde olup olmadığını sormayı gerektirir. Bunu, pikselden dairenin merkezine olan mesafeyi hesaplayarak yaparız.
Bu mesafeyi hesaplamanın birkaç yolu vardır. En kolay olanı, dahili olarak iki nokta arasındaki farkın length()'ini hesaplayan distance() fonksiyonunu kullanır (bizim durumumuzda piksel koordinatı ve tuvalin merkezi). length() fonksiyonu, dahili olarak karekök (sqrt()) kullanan hipotenüs denkleminin bir kısayolundan başka bir şey değildir.
Tuvalin merkezine olan mesafeyi hesaplamak için distance(), length() veya sqrt() kullanabilirsiniz. Aşağıdaki kod bu üç fonksiyonu ve her birinin tam olarak aynı sonucu döndürdüğü şaşırtıcı olmayan gerçeği içerir.
* Aynı sonucu elde etmenin farklı yollarını denemek için satırları yorum satırı yapın ve yorumdan çıkarın.
<div class="codeAndCanvas" data="circle-making.frag"></div>
Önceki örnekte tuvalin merkezine olan mesafeyi pikselin renk parlaklığına eşledik. Bir piksel merkeze ne kadar yakınsa, o kadar düşük (koyu) değere sahiptir. Merkezden (vec2(0.5, 0.5)) maksimum mesafe 0.5'i zar zor aştığı için değerlerin çok yükselmediğine dikkat edin. Bu haritayı düşünün:
* Bundan ne çıkarabilirsiniz?
* Bunu bir daire çizmek için nasıl kullanabiliriz?
* Tüm dairesel gradyanı tuvalin içinde tutmak için yukarıdaki kodu değiştirin.
Mesafe alanı
Yukarıdaki örneği, koyunun daha yüksek anlamına geldiği bir yükseklik haritası olarak da düşünebiliriz. Gradyan bize bir koninin oluşturduğu desene benzer bir şey gösterir. Kendinizi o koninin tepesinde hayal edin. Koninin kenarına olan yatay mesafe 0.5'tir. Bu tüm yönlerde sabit olacaktır. Koniyi nerede "keseceğinizi" seçerek daha büyük veya daha küçük bir dairesel yüzey elde edeceksiniz.
Temel olarak, şekiller yapmak için uzayın bir yeniden yorumunu (merkeze olan mesafeye dayalı) kullanıyoruz. Bu teknik "mesafe alanı" olarak bilinir ve yazı tipi taslağlarından 3D grafiklere kadar farklı şekillerde kullanılır.
Aşağıdaki alıştırmaları deneyin:
* 0.5'in üstündeki her şeyi beyaza ve altındaki her şeyi 0.0'a çevirmek için step() kullanın.
* Arka plan ve ön plan renklerini tersine çevirin.
* smoothstep() kullanarak, dairenizde güzel düzgün kenarlıklar elde etmek için farklı değerlerle denemeler yapın.
* Bir uygulamadan memnun olduğunuzda, gelecekte yeniden kullanabileceğiniz bir fonksiyon yapın.
* Daireye renk ekleyin.
* Atan bir kalbi simüle ederek dairenizi büyüyüp küçülecek şekilde canlandırabilir misiniz? (Önceki bölümdeki animasyondan ilham alabilirsiniz.)
* Bu daireyi hareket ettirmeye ne dersiniz? Tek bir tuvalde farklı daireleri hareket ettirip yerleştirebilir misiniz?
* Mesafe alanlarını farklı fonksiyonlar ve işlemler kullanarak birleştirirseniz ne olur?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Bu tekniği kullanarak üç kompozisyon yapın. Animasyonlu olurlarsa daha da iyi!
#### Araç kutunuz için
Hesaplama gücü açısından sqrt() fonksiyonu — ve ona bağlı tüm fonksiyonlar — pahalı olabilir. İşte dot() çarpımını kullanarak dairesel bir mesafe alanı oluşturmanın başka bir yolu.
<div class="codeAndCanvas" data="circle.frag"></div>
Mesafe alanının faydalı özellikleri
Mesafe alanları neredeyse her şeyi çizmek için kullanılabilir. Açıkçası bir şekil ne kadar karmaşıksa, denklemi de o kadar karmaşık olacaktır, ancak belirli bir şeklin mesafe alanlarını yapma formülüne sahip olduğunuzda, düzgün kenarlar ve çoklu dış hatlar gibi efektleri birleştirmek ve/veya uygulamak çok kolaydır. Bu nedenle mesafe alanları yazı tipi oluşturmada popülerdir, örneğin Mapbox GL Labels, Matt DesLauriers Material Design Fonts ve iPhone 3D Programming'in 7. Bölümünde, O'Reilly tarif edildiği gibi.
Aşağıdaki koda bir göz atın.
<div class="codeAndCanvas" data="rect-df.frag"></div>
Koordinat sistemini merkeze taşıyarak ve konum değerlerini -1 ile 1 arasında yeniden eşlemek için yarıya küçülterek başlıyoruz. Ayrıca 24. satırda mesafe alanı değerlerini fract() fonksiyonuyla görselleştiriyoruz ve oluşturdukları deseni görmeyi kolaylaştırıyoruz. Mesafe alanı deseni bir Zen bahçesindeki halkalar gibi tekrar tekrar tekrarlanır.
19. satırdaki mesafe alanı formülüne bir bakalım. Orada dört çeyrekte de (.3,.3) veya vec3(.3) konumuna olan mesafeyi hesaplıyoruz (orada abs()'in yaptığı budur).
20. satırın yorumunu kaldırırsanız, bu dört noktaya olan mesafeleri min() kullanarak sıfıra birleştirdiğimizi not edeceksiniz. Sonuç ilginç yeni bir desen üretir.
Şimdi 21. satırın yorumunu kaldırmayı deneyin; aynı şeyi ama max() fonksiyonunu kullanarak yapıyoruz. Sonuç yuvarlatılmış köşeli bir dikdörtgendir. Mesafe alanı halkaların merkezden uzaklaştıkça nasıl daha düzgün hale geldiğine dikkat edin.
27-29 arası satırların yorumunu teker teker kaldırarak mesafe alanı deseninin farklı kullanımlarını anlayın.
Kutupsal şekiller
Renkler bölümünde Kartezyen koordinatları, her pikselin yarıçapını ve açısını aşağıdaki formülle hesaplayarak kutupsal koordinatlara eşledik:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);Bu formülün bir kısmını bölümün başında bir daire çizmek için kullandık. length() kullanarak merkeze olan mesafeyi hesapladık. Artık mesafe alanları hakkında bilgi sahibi olduğumuza göre, kutupsal koordinatlar kullanarak şekil çizmenin başka bir yolunu öğrenebiliriz.
Bu teknik biraz kısıtlayıcıdır ama çok basittir. Farklı şekiller elde etmek için bir dairenin yarıçapını açıya bağlı olarak değiştirmekten oluşur. Modülasyon nasıl çalışır? Evet, şekillendirme fonksiyonları kullanarak!
Aşağıda aynı fonksiyonları Kartezyen grafikte ve kutupsal koordinatlar shader örneğinde (21 ile 25 arası satırlar) bulacaksınız. Bir koordinat sistemi ile diğeri arasındaki ilişkiye dikkat ederek fonksiyonların yorumunu teker teker kaldırın.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Deneyin:
* Bu şekilleri canlandırın.
Şekilde delikler açmak* için farklı şekillendirme fonksiyonlarını birleştirerek çiçekler, kar taneleri ve dişliler yapın.
Şekillendirme Fonksiyonları Bölümünde* kullandığımız plot() fonksiyonunu kullanarak sadece konturu çizin.
Güçleri birleştirme
Artık farklı şekiller çizmek için atan() kullanarak bir dairenin yarıçapını açıya göre nasıl modüle edeceğimizi öğrendiğimize göre, atan()'ı mesafe alanlarıyla kullanmayı ve mesafe alanlarıyla mümkün olan tüm hile ve efektleri uygulamayı öğrenebiliriz.
İşin püf noktası, kutupsal koordinatlar kullanarak bir çokgenin kenar sayısını mesafe alanını oluşturmak için kullanmaktır. Andrew Baldwin'dan aşağıdaki koda göz atın.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Bu örneği kullanarak, istenen bir şeklin konumunu ve köşe sayısını girdi olarak alan ve bir mesafe alanı değeri döndüren bir fonksiyon yapın.
* min() ve max() kullanarak mesafe alanlarını birbirine karıştırın.
* Mesafe alanlarını kullanarak çoğaltmak için geometrik bir logo seçin.
Tebrikler! Zorlu kısımı atlatınız! Bir mola verin ve bu kavramların yerleşmesine izin verin -- Processing'de basit şekiller çizmek kolaydır ama burada değil. Shader dünyasında şekil çizme sıra dışıdır ve bu yeni kodlama paradigmasına uyum sağlamak yorucu olabilir.
#### Araç kutunuz için
* LYGIA'nın çizim fonksiyonları 2D şekiller ve desenler çizmek için yeniden kullanılabilir fonksiyonlar kümesidir. Mesafe alanları aracılığıyla daha karmaşık şekilleri birleştirmek için LYGIA'nın SDF fonksiyonları klasörünü de keşfedebilirsiniz. Yeniden kullanılabilirlik, performans ve esneklik için tasarlanmış çok ayrıntılı bir kütüphanedir. Ve herhangi bir projeye ve framework'e kolayca eklenebilir.
* Bu bölümün sonunda PixelSpirit Deck bağlantısını bulacaksınız, bu kart destesi yeni SDF fonksiyonlarını öğrenmenize, bunları tasarımlarınıza oluşturmanıza ve shader'larınızda kullanmanıza yardımcı olacaktır. Deste ilerlemeye dayalı bir öğrenme eğrisine sahiptir, bu yüzden her gün bir kart alıp üzerinde çalışmak becerilerinizi aylarca zorlayacaktır.
Artık şekil çizmeyi bildiğinize göre, aklınıza yeni fikirler geleceğinden eminim. Bir sonraki bölümde şekilleri nasıl taşıyacağınızı, döndüreceğinizi ve ölçeklendireceğinizi öğreneceksiniz. Bu, kompozisyonlar yapmanıza olanak tanıyacak!
---
07/README Vi (07/README-vi.md)
Hình dáng
Cuối cùng thì cũng tới phần này! Ta đã trang bị biết bao kiến thức chỉ để chờ giây phút này thôi đấy! Hầu hết những kiến thức nền tảng của GLSL đều đã được giới thiệu ở các chương trước. Giờ là lúc để kết hợp tất cả những kiến thức đó lại. Ở chương này ta sẽ học cách vẽ những hình khối cơ bản nhất, bằng code, chạy song song trên các bộ vi xử lý của GPU.
Hình chữ nhật
Cứ tưởng tượng rằng ta có một tờ giấy kẻ ô sẵn vẫn hay dùng trong môn Toán và ta phải làm bài tập về nhà là vẽ một hình vuông lên đó. Kích thước của tờ giấy là 10x10 còn hình vuông sẽ cỡ 8x8. Làm thế nào nhỉ ?
Bạn sẽ tô màu tất cả các ô của tờ giấy trừ những ô ở dòng trên cùng và dưới cùng, và cả cột đầu tiên lẫn cột cuối cùng nữa, phải không ?
Điều này thì có liên quan gì tới shader ? Mỗi ô vuông nhỏ trên tờ giấy đó có thể coi như một điểm ảnh hay một thread. Ta biết vị trí của từng ô vuông đó, giống như bàn cờ vua ấy mà. Ở các chương trước ta đã ánh xạ toạ độ x và y vào các kênh màu đỏ và xanh lá, và ta cũng đã biết cách để đưa các toạ độ đó về trong khoảng [0.0, 1.0] mà vẫn giữ nguyên tỉ lệ. Áp dụng những điều đó như thế nào để vẽ được hình vuông ở chính giữa canvas bây giờ ?
Hãy bắt đầu bằng đoạn code giả dưới đây có sử dụng lệnh if để kiểm tra từng ô một. Cách làm này cũng giống hệt như cách mà ta chọn các ô sẽ tô màu trên giấy.
if ( (X > 1) AND (Y > 1) )
tô màu trắng
else
tô màu đenNhưng ta biết có một cách khác, tốt hơn, đó là dùng hàm step(), và thay vì dùng kích thước 10x10 thì ta sẽ dùng các toạ độ đã được chuẩn hoá trong khoảng [0.0, 1.0]
uniform vec2 u_resolution;void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// Mỗi dòng dưới đây sẽ cho kết quả 1.0 hoặc 0.0
float left = step(0.1,st.x); // Tương đương với ( X > 0.1 )
float bottom = step(0.1,st.y); // Tương đương với ( Y > 0.1 )
// Phép nhân left*bottom tương đương với điều kiện logic AND
color = vec3( left * bottom );
gl_FragColor = vec4(color,1.0);
}
Hàm step() sẽ chọn màu đen (vec3(0.0)) cho tất cả các điểm ảnh có toạ độ dưới 0.1, và màu trắng (vec3(1.0)) cho tất cả các điểm ảnh còn lại. Phép nhân giữa left và bottom cũng tương đương với lệnh điều kiện logic AND tức là cả 2 giá trị phải bằng 1.0 thì kết quả của phép nhân mới bằng 1.0 được, các trường hợp khác đều cho kết quả 0.0. Đoạn code phía trên sẽ vẽ 2 đường thẳng màu đen, một đường ở phía dưới cùng của canvas, và đường còn lại nằm ở mép trái của canvas.
Trong đoạn code phía trên, ta phải sử dụng hàm step() hai lần, mỗi lần cho một trục. Để rút gọn hơn, ta có thể truyền cả 2 toạ độ vào hàm đó cùng lúc như sau:
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;Cho tới lúc này, ta mới chỉ vẽ được 2 đường viền ở dưới cùng và bên trái của hình vuông. Hãy vẽ nốt 2 đường viền còn lại ở phía trên và bên phải nào:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Hãy uncomment dòng 21-22, bạn sẽ thấy tôi đã đảo ngược giá trị của toạ độ st rồi áp dụng y nguyên hàm step() như code cũ. Bằng phép đảo ngược này thì điểm vec2(0.0,0.0) sẽ đại diện cho góc trên cùng bên phải. Hay nói cách khác, tôi đã sử dụng phép chiếu đối xứng.
Chú ý: Ở dòng 18 và 22 tất cả kết quả thu được từ 2 hàm step() được nhân với nhau. Đoạn code dưới đây cũng sẽ có tác dụng tương tự:
vec2 bl = step(vec2(0.1),st); // Tô màu các ô ở đường viền dưới cùng và bên trái
vec2 tr = step(vec2(0.1),1.0-st); // Tô màu các ô ở đường viền trên cùng và bên phải
color = vec3(bl.x bl.y tr.x * tr.y);Ô hay nhỉ ? Toàn bộ bài này chỉ dùng mỗi hàm step() rồi nhân kết quả lại với nhau, kèm theo phép chiếu đối xứng qua tâm.
Trước khi sang phần tiếp theo, hãy thử:
* Thay đổi kích thước của hình vuông / chữ nhật màu trắng
* Thử dùng hàm smoothstep() thay vì step(). Chú ý là khi thay đổi giá trị, ta sẽ thấy các cạnh của hình chữ nhật sẽ thay đổi từ rõ nét sang mờ dần.
* Thử dùng cách khác để vẽ, ví dụ như hàm floor().
* Chọn một cách vẽ bạn thích nhất và biến nó thành 1 hàm mà bạn có thể sử dụng lại trong tương lai. Cố gắng giữ cho hàm đó thật linh hoạt và hiệu quả nhé.
* Tạo một hàm khác chỉ vẽ mỗi đường viền thôi
* Nghĩ xem làm cách nào để đặt nhiều hình chữ nhật có màu sắc và kích thước khác nhau cùng vào trong một canvas nhé ? Nếu bạn nghĩ được cách rồi thì thử dựng lại bức tranh nổi tiếng của Piet Mondrian nhé.
Hình tròn
Vẽ hình vuông trên tờ giấy kẻ ô sẵn hay vẽ hình chữ nhật trong hệ toạ độ Đề-các rất dễ, nhưng để vẽ hình tròn thì ta phải tìm cách khác, đặc biệt là phải chú ý tới từng điểm ảnh một. Một trong những giải pháp được đưa ra là remap toạ độ không gian về một hệ quy chiếu nào đó dựa trên khoảng cách tới tâm rồi dùng hàm step() để loại bỏ các điểm ảnh nằm quá xa tâm.
Quay lại với môn Toán nào, ta sẽ cần 1 tờ giấy có kẻ ô sẵn và 1 chiếc compa. Chỉ cần xoay 1 vòng compa là ta có ngay 1 đường tròn, giờ thì chỉ việc tô màu bên trong đường tròn là được.
Nếu coi mỗi ô vuông kẻ sẵn trên giấy là 1 điểm ảnh trên canvas, thì để áp dụng cách vẽ tương tự như trên vào trong shader, thực tế ta sẽ phải kiểm tra từng ô một xem nó có nằm bên trong chu vi đường tròn không. Ta có thể biết được điều này bằng cách tính khoảng cách của điểm ảnh đó tới tâm đường tròn.
Có vài cách để tính khoảng cách và cách dễ nhất là dùng hàm distance(), mà bên trong nó sẽ dùng hàm length() để tính độ dài đoạn thẳng nối giữa mỗi điểm ảnh với tâm hình tròn. Để tính độ dài 1 đoạn thẳng, hàm length() sẽ coi đoạn thẳng đó là cạnh huyền của một tam giác rồi áp dụng định lý Pythagores là xong, và công thức đó cần tới hàm căn bậc 2 (sqrt()).
Bạn có thể dùng hàm distance(), length() hay sqrt() tuỳ ý để tính khoảng cách của từng điểm ảnh tới tâm canvas. Đoạn code dưới đây dùng cả 3 hàm trên và không có gì ngạc nhiên là kết quả của chúng đều giống nhau.
* Hãy comment và uncomment từng cách tính để kiểm tra xem có đúng là chúng cho ra cùng 1 kết quả không nhé
<div class="codeAndCanvas" data="circle-making.frag"></div>
Đoạn code trên minh hoạ khoảng cách từ mỗi điểm ảnh tới tâm canvas bằng màu sắc. Càng gần tâm thì màu càng tối. Ngay cả những điểm xa nhất thì cũng không sáng quá, vì giá trị tối đa chỉ có thể là vec2(0.5, 0.5). Hãy quan sát hình minh hoạ đó và tự hỏi:
* Mình có thể suy luận những gì từ hình này ?
* Làm thế nào để biến nó thành hình tròn ?
* Sửa đoạn code trên để toàn bộ dải màu đen trắng nằm trong canvas thay vì chỉ có nửa tối như hiện tại
Distance field
Nếu coi hình minh hoạ từ ví dụ trên là 1 bản đồ địa hình đo chiều cao, và vùng tối màu là vùng cao hơn, thì ta có thể tưởng tượng ra địa hình đó giống như hình nón. Ở mặt đất thì khoảng cách từ tâm tới mọi điểm khác cùng mặt phẳng đều là 0.5. Vậy nếu ta bổ ngang hình nón tại một độ cao nào đó, thì ta sẽ có 1 lát cắt hình tròn có bán kính tương ứng với độ cao. Càng gần mặt đất thì lát cắt càng lớn. Và ở chiều ngược lại, nếu ta chồng rất nhiều lát cắt hình tròn lên nhau theo thứ tự của bán kính thì ta cũng sẽ thu được 1 hình nón.
Kỹ thuật này được gọi là "distance field" (trường khoảng cách), và được ứng dụng rất nhiều, từ việc vẽ viền cho các phông chữ cho tới đồ hoạ 3 chiều.
Hãy thử:
* Dùng hàm step() tô màu trắng cho tất cả các điểm xa tâm hơn 0.5 đơn vị, các điểm ảnh còn lại thì màu đen.
* Đảo ngược màu của 2 vùng kể trên.
* Dùng hàm smoothstep(), và điều chỉnh tham số sao cho thu được 1 đường viền có độ mờ mong muốn.
* Biến nó thành 1 hàm mà bạn có thể sử dụng lại trong tương lai. Cố gắng giữ cho hàm đó thật linh hoạt và hiệu quả nhé.
* Tô màu khác cho hình tròn.
* Bạn có thể làm cho hình tròn to ra rồi nhỏ lại theo nhịp tim không ? (Hãy lấy cảm hứng từ chuyển động của chương trước nhé).
* Vậy nếu muốn đặt hình tròn ở các vị trí khác nhau thay vì chỉ ở chính giữa thì sao ?
* Điều gì sẽ xảy ra nếu ta có nhiều hơn 1 distance field và kết hợp chúng lại bằng các hàm toán học cơ bản như dưới đây ?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));* Hãy tạo ra ba hình cấu tạo từ các phép toán kể trên. Nếu nó chuyển động được thì càng tốt.
#### Tiện ích
Thực ra thì hàm sqrt() và các hàm gián tiếp sử dụng nó nữa, khá là chậm. Đây là 1 cách khác để tính khoảng cách tới tâm của đường tròn bằng cách tính tích vô hướng của vector dựa vào hàm dot().
<div class="codeAndCanvas" data="circle.frag"></div>
Các thuộc tính hữu ích của Distance Field
Các distance field có thể được kết hợp lại với nhau để vẽ hầu như tất cả mọi thứ. Rõ ràng là hình vẽ càng phức tạp thì phương pháp kết hợp chúng sẽ càng rối rắm, nhưng nếu bạn có 1 công thức để tính 1 distance field cho 1 khối hình học cơ bản nào đó rồi, thì việc tạo thêm hiệu ứng cho nó lại rất dễ, uốn cong các cạnh hay vẽ nhiều đường viền cùng lúc chẳng hạn. Chình vì điều này mà distance field là kỹ thuật rất phổ biến khi cần vẽ các ký tự của các phông chữ khác nhau, ví dụ như các phần mềm Mapbox GL Labels, Matt DesLauriers Material Design Fonts và được mô tả chi tiết trong chương 7 của quyển sách iPhone 3D Programming của nhà xuất bản O’Reilly.
Hãy xem đoạn code sau:
<div class="codeAndCanvas" data="rect-df.frag"></div>
Đầu tiên tôi đặt gốc của trục toạ độ ở tâm của canvas rồi remap toạ độ lại để chúng nằm trong khoảng [-1, 1]. Ở dòng 24 tôi có minh hoạ giá trị của các distance field bằng hàm fract() để bạn nhìn rõ tâm của các distance field và tạo nên nhiều đường tròn lồng nhau giống như những cái vòng trên cát trong một khu vườn Thiền vậy.
Hãy nhìn công thức tính distance field ở dòng 19. Tôi tính khoảng cách từ mỗi điểm tới vị trí (.3,.3) viết tắt là vec3(.3), cho mỗi góc phần tư riêng biệt (hàm abs() đã giúp tôi tách riêng 4 góc phần tư ra).
Nếu bạn uncomment dòng 20, bạn sẽ nhận thấy bằng cách dùng hàm min() tôi đã tạo ra hoa văn mới.
Hãy uncomment tiếp dòng 21; tôi dùng hàm max() thay cho hàm min() ở trên. Kết quả là thu được một hình chữ nhật có 4 góc được bo tròn. Hơn thế nữa, càng xa tâm thì 4 góc ngày càng được bo tròn hơn.
Cuối cùng, hãy lần lượt uncomment từng dòng từ 27 tới 29 để thấy các phương pháp vẽ hoa văn khác nhau từ distance field.
Polar shapes - Các hình được tạo ra từ hệ toạ độ cực
Ở chương về màu sắc ta đã chuyển hệ quy chiếu từ hệ toạ độ Đề-các sang hệ toạ độ cực bằng cách tính khoảng cách và góc của mỗi điểm ảnh tới tâm bằng công thức sau:
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);Ta cũng đã dùng một phần của công thức này để vẽ hình tròn, cụ thể là dùng hàm length() để tính khoảng cách tới tâm hình tròn. Với kiến thức về distance field, ta có thể áp dụng để vẽ nhiều hình khác trên hệ toạ độ cực.
Kỹ thuật này rất đơn giản, chỉ cần thay đổi khoảng cách tới tâm dựa vào góc nghiêng, là ta sẽ có các hình khác nhau. Nhưng từ góc nghiêng làm thế nào để tính ra khoảng cách mong muốn nhỉ ? Đương nhiên phải dùng hàm số rồi.
Dưới đây là đồ thị của các hàm số được vẽ trên hệ toạ độ Đề-các, còn ở đoạn code sau đó là vẽ trên hệ toạ độ cực (từ dòng 21 tới 25). Hãy uncomment từng hàm số một và chú ý tìm xem mỗi điểm ở hệ toạ độ này nằm ở đâu trong hệ toạ độ kia.
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x2.5))0.5+0.3;
//y = abs(cos(x12.)sin(x3.)).8+.1;
//y = smoothstep(-.5,1., cos(x10.))0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Hãy thử:
* Làm những hình này chuyển động.
Kết hợp thêm vaì hàm số nữa để đục lỗ* những hình trên, có thể sẽ tạo ra hình bông hoa, bông tuyết hay bánh răng.
Sử dụng hàm plot() từ Chương Các hàm số cơ bản* để chỉ vẽ mỗi đường viền thôi thay vì tô màu đặc.
Kết hợp các kiến thức đã học
Ta đã học cách ánh xạ góc nghiêng của hệ toạ độ cực sang khoảng cách để vẽ các hình thú vị, giờ hãy thử áp dụng tất cả những gì đã học với hàm atan() xem sao.
Mẹo ở đây là ta sẽ dựng các distance field dựa vào số đỉnh của đa giác. Hãy xem đoạn code này của Andrew Baldwin.
<div class="codeAndCanvas" data="shapes.frag"></div>
* Dựa vào ví dụ trên đây, hãy tạo một hàm nhận tham số đầu vào là vị trí của tâm và số đỉnh của đa giác để tính distance field nhằm dựng nên đa giác đó.
* Kết hợp các distance field bằng hàm min() và max().
* Vẽ lại một logo hình học nào đó bằng distance field
Xin chúc mừng! Bạn đã vượt qua được phần khó nhằn này rồi ! Hãy tạm nghỉ để suy ngẫm dần các khái niệm này. Vẽ các hình này bằng shader rất thú vị nhưng cũng rất loằng ngoằng.
Đây là URL tới PixelSpirit Deck. Một bộ bài có các biểu tượng được tạo ra bởi các hàm SDF (Signed Distance Field). Bạn hãy thử vẽ lại các biểu tượng đó bằng shader nhé. Các hình vẽ sẽ khó dần lên đấy, thế nên cứ từ từ, mỗi ngày vẽ một hình cũng đủ để mài dũa kỹ năng của bạn về Distance Field ngày càng điêu luyện hơn rồi.
Ở chương tới, chúng ta sẽ học cách di chuyển, quay và thay đổi kích thước của các hình. Từ đó bạn có thể ghép chúng lại thành những hình phức tạp hơn.
---
07/SUMMARY Pt (07/SUMMARY-pt.md)
Vamos dar uma olhada em como desenhar formar simples de forma procedural paralela. Em poucas palavras, tudo o que você precisa é determinar se cada pixel pertence à forma que quer desenhar ou não, e aplicar cores diferentes dependendo do caso. Você pode usar o sistema de coordenadas, como um papel quadriculado, para desenhar retângulos e quadrados. Vamos olhar um conceito mais avançado chamado campo de distância para desenhar formar mais complexas.
---
07/SUMMARY (07/SUMMARY.md)
Let's look at how to draw simple shapes in a parallel procedural way. In a nutshell, all you need to do is to determine if each pixel belongs to the shape you want to draw or not, and apply different colors accordingly. You can use coordinate system like a grid paper to draw rectangles and squares. We'll look at more advanced concept called distance field to draw more complex shapes.
---
08/README (08/README.md)
2D Matrices
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Translate
In the previous chapter we learned how to make some shapes - the trick to moving those shapes is to move the coordinate system itself. We can achieve that by simply adding a vector to the `st` variable that contains the location of each fragment. This causes the whole space coordinate system to move.
This is easier to see than to explain, so to see for yourself:
* Uncomment line 35 of the code below to see how the space itself moves around.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Now try the following exercise:
* Using `u_time` together with the shaping functions move the small cross around in an interesting way. Search for a specific quality of motion you are interested in and try to make the cross move in the same way. Recording something from the "real world" first might be useful - it could be the coming and going of waves, a pendulum movement, a bouncing ball, a car accelerating, a bicycle stopping.
Rotations
To rotate objects we also need to move the entire space system. For that we are going to use a matrix. A matrix is an organized set of numbers in columns and rows. Vectors are multiplied by matrices following a precise set of rules in order to modify the values of the vector in a particular way.
[](https://en.wikipedia.org/wiki/Matrix)
GLSL has native support for two, three and four dimensional matrices: `mat2` (2x2), `mat3` (3x3) and `mat4` (4x4). GLSL also supports matrix multiplication (`*`) and a matrix specific function (`matrixCompMult()`).
Based on how matrices behave it's possible to construct matrices to produce specific behaviors. For example we can use a matrix to translate a vector:
More interestingly, we can use a matrix to rotate the coordinate system:
Take a look at the following code for a function that constructs a 2D rotation matrix. This function follows the above formula for two dimensional vectors to rotate the coordinates around the `vec2(0.0)` point.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}According to the way we've been drawing shapes, this is not exactly what we want. Our cross shape is drawn in the center of the canvas which corresponds to the position `vec2(0.5)`. So, before we rotate the space we need to move shape from the center to the `vec2(0.0)` coordinate, rotate the space, then finally move it back to the original place.
That looks like the following code:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Try the following exercises:
* Uncomment line 45 of above code and pay attention to what happens.
* Comment the translations before and after the rotation, on lines 37 and 39, and observe the consequences.
* Use rotations to improve the animation you simulated in the translation exercise.
Scale
We've seen how matrices are used to translate and rotate objects in space. (Or more precisely to transform the coordinate system to rotate and move the objects.) If you've used 3D modeling software or the push and pop matrix functions in Processing, you will know that matrices can also be used to scale the size of an object.
Following the previous formula, we can figure out how to make a 2D scaling matrix:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Try the following exercises to understand more deeply how this works.
* Uncomment line 42 of above code to see the space coordinate being scaled.
* See what happens when you comment the translations before and after the scaling on lines 37 and 39.
* Try combining a rotation matrix together with a scale matrix. Be aware that the order matters. Multiply by the matrix first and then multiply the vectors.
* Now that you know how to draw different shapes, and move, rotate and scale them, it's time to make a nice composition. Design and construct a fake UI or HUD (heads up display). Use the following ShaderToy example by Ndel for inspiration and reference.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Other uses for matrices: YUV color
YUV is a color space used for analog encoding of photos and videos that takes into account the range of human perception to reduce the bandwidth of chrominance components.
The following code is an interesting opportunity to use matrix operations in GLSL to transform colors from one mode to another.
<div class="codeAndCanvas" data="yuv.frag"></div>
As you can see we are treating colors as vectors by multiplying them with matrices. In that way we “move” the values around.
In this chapter we've learned how to use matrix transformations to move, rotate and scale vectors. These transformations will be essential for making compositions out of the shapes we learned about in the previous chapter. In the next chapter we'll apply all we've learned to make beautiful procedural patterns. You will find that coding repetition and variation can be an exciting practice.
#### For your toolbox
* LYGIA's space functions are set of reusable functions to manipulate space in GLSL. It's a great resource to learn how to use matrices to manipulate space. It's very granular library, designed for reusability, performance and flexibility. And it can be easily be added to any projects and frameworks.
---
08/README De (08/README-de.md)
2D-Matrizen
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Verschieben
Im letzten Kapitel haben wir gesehen, wie man unterschiedliche Formen zeichnet. Der Trick, um diese Formen auf der Zeichenfläche beliebig zu positionieren, besteht nun darin, das Koordinatensystem „unterhalb“ dieser Formen zu verschieben. Wir erreichen dies, indem wir einfach einen Vektor zu der `st`-Variable addieren, die die Lage des jeweiligen Objekts beim Zeichnen bestimmt. Dadurch verschiebt sich das gesamte Koordinatensystem.
Es ist vermutlich einfacher, das Verfahren in der Praxis nachzuvollziehen, als es in der Theorie zu studieren. Schau einfach selbst:
Entferne die Kommentarzeichen in der Programmzeile 35*, und Du wirst sehen, wie sich das Zeichenobjekt gemeinsam mit dem darunterliegenden Koordinatenraum bewegt.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Jetzt versuche Dich an der folgenden Aufgabe:
* Nutze `u_time` in Verbindung mit einer formgebenden Funktion, um das kleine Kreuz auf interessante Weise über die Zeichenfläche zu bewegen. Das Vorbild dafür kann durchaus aus der realen Welt stammen, beispielsweise eine Wellenbewegung, das Schwingen eines Pendels, ein springender Ball, ein beschleunigendes Auto oder ein stoppendes Fahrrad.
Rotationen
Um Objekte rotieren zu lassen, müssen wir ebenfalls nur das zugrundeliegende Koordinatensystem transformieren. Wir verwenden dafür eine Rotationsmatrix). Eine Matrix ist eine geordnete Anordnung von Zahlen in Form von Zeilen und Spalten. Vektoren werden mit Matrizen nach einem festen vorgegebenen Verfahren multipliziert, um den Vektor auf eine bestimmte Art und Weise zu verändern.
[](http://de.wikipedia.org/wiki/Matrizenmultiplikation)
GLSL verfügt über eingebaute Unterstützung für zwei-, drei- und vierdimensionale Matrizen: `mat2` (2x2), `mat3` (3x3) und `mat4` (4x4). Außerdem unterstützt GLSL die Matrizenmultiplikation (`*`) mit einem Vektor sowie die Multiplikation zweier Matrizen mit der speziellen Funktion (`matrixCompMult()`).
Darauf basierend, wie Matrizen funktionieren, kann man Matrizen mit einem bestimmten „Verhalten“ konstruieren. Beispielsweise können wir eine Matrix zum Verschieben eines Vektors nutzen:
Noch interessanter wird es, wenn wir eine Matrix zur Drehung des Koordinatensystems verwenden:
Schau Dir den folgenden Programmcode an, der die benötigte Matrix für eine Rotation des Koordinatensystems um einen vorgegebenen Winkel produziert. Diese Funktion folgt der obigen Formel für zweidimensionale Vektoren, um die Koordinaten um einen `vec2(0.0)`-Punkt rotieren zu lassen.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}In Bezug auf die Art und Weise, wie wir Formen bislang gezeichnet haben, ist das noch nicht ganz das, was wir benötigen. Unser Kreuz wird auf der Mitte der Zeichenfläche generiert, ausgehend vom Punkt `vec2(0.5)`. Vor der Rotation müssen wir die Form deshalb von der Mitte des Koordinatensystem zum `vec2(0.0)` verschieben, anschließend die Drehung ausführen und das Objekt dann wieder an seinen ursprünglichen Platz bringen.
In Programmcode „gegossen“, sieht das dann wie folgt aus:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Probiere die folgenden Übungen aus:
Entferne die Kommentarzeichnen aus der Programmzeile 45* und beobachte, was dann passiert.
Kommentiere das Verschieben vor und nach der Rotation in den Zeilen 37 und 39* aus und beobachte die Konsequenzen.
* Nutze Rotationen, um die Animation zu verbessern, die Du beim Beispiel zum Verschieben erstellt hast.
Skalieren
Wir haben gesehen, wie man die Verschiebung und die Rotation von Objekten realisiert, indem man mit Matrizen auf den Koordinatenraum einwirkt. Falls Du bereits mit 3D-Modellierungssoftware oder den Matrizenfunktionen in Procesing gearbeitet hast, weißt Du wahrscheinlich, dass man Matrizen auch für die Skalierung von Objekten verwenden kann.
Indem wir uns an der obigen Formel orientieren, können wir eine 2D-Saklierungsmatrix für das gewünschte Vergrößern oder Verkleinern erstellen:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Probiere die folgenden Übungen aus, um noch besser zu verstehen, wie das Ganze funktioniert.
Entferne die Kommentare in der Programmzeile 42*, um zu sehen, wie der Koordinatenraum skaliert wird.
Schau Dir an, was geschieht, wenn Du die Verschiebung vor und nach der Skalierung in den Programmzeilen 37 und 39* entfernst.
* Probiere eine Rotationsmatrix in Verbindung mit einer Skalierungsmatrix aus. Sei Dir bewusst, dass die Reihenfolge der Matrixoperationen dabei eine entscheidende Rolle spielt.
* Jetzt, wo Du weißt, wie man unterschiedliche Formen malt, verschiebt, rotiert und skaliert, ist es an der Zeit, eine etwas komplexere Komposition zu erstellen. Designe und entwickle die Simulation einer futuristischen Benutzeroberfläche bzw. eines Head-up-Displays. Nutze das folgende Beispiel aus den Shadertoys von Ndel als Inspirationsquelle und als Referenz.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Ein anderes Einsatzgebiet für Matrizen: YUV-Farben
YUV ist ein Farbmodell, das für die analoge Kodierung von Fotos und Videos verwendet wird. Das Modell berücksichtigt die Grenzen der menschlichen Farbwahrnehmung und nutzt dies für die Beschränkung der Bandbreite (der benötigten Bits) bei der Kodierung der Farbigkeit (Chrominanz).
Der folgende Programmcode zeigt eine interessante Einsatzmöglichkeit für den Einsatz von Matrizenfunktionen zur Konvertierung von Farben zwischen zwei Farbräumen bzw. Farbsystemen.
<div class="codeAndCanvas" data="yuv.frag"></div>
Wie Du siehst, behandeln wir Farben hier als Vektoren, indem wir sie mit Matrizen multiplizieren. Auf diese Weise können wir die Farben über ihre Farbwerte hin und her bewegen.
In diesem Kapitel haben wir gelernt, wie man Matrixtransformationen für das Verschieben, Rotieren und Skalieren von Vektoren nutzt. Für die Erstellung von Kompositionen aus mehreren grafischen Formen und Objekten sind diese Transformationen essenziell. Im nächsten Kapitel werden wir all das Erlernte anwenden, um bezaubernde prozedurale Muster zu erstellen. Du wirst sehen, dass die Wiederholung und Variation von Programmcode höchst interessante Resultate liefern kann.
---
08/README Fa (08/README-fa.md)
ماتزیس های 2D
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
انتقال
در قسمت های قبل یاد گرفتیم چگونه اشکل مختلف بسازیم - در اصل ترفند برای حرکت دادن اشکال، حرکت دادن خود محور های مختصات است. میتوان با اضافه کردن یک بردار به `st` که مقدار محل هر پیکسل را در خود نگه داشته است، این ترفند را انجام داد. با این کار تمام فضای مختصاتی حرکت میکند.
دیدن شکل بالا آسان تر از توضیح است، پس خودتان ببینید.:
* خط 35 را از حالت کامنت خارج کنید، تا ببینید چگونه فضا خود به خود حرکت میکند.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
حالا تمرین زیر را انجام دهید:
* از `u_time` همراه با توابع شکل دهی، طوری استفاده کنید، که شکل بالا به طور جالبت تری حرکت بکند. حرکت خاصی که در نظر دارید را جستجو کنید و سعی کنید حرکت شکل بالا را مثل آن شبیه یازی کنید. ایده گرفتن از حرکات دنیای واقعی میتواند مفید باشد، مثلا حرکت آونگ، توپ، شتاب گرفتن ماشین یا توقف دوچرخه.
چرخش
برای چرخاندن یک شکل، باز هم نیاز است تمام فضا را بچرخانیم. برای این کار از matrix استفاده میکنیم. ماتریس مجموعه ای متشکل از اعداد در سطر ها ستون هاست. بردار ها با قوانین خاصی در ماتریس ها میتوانند ضرب شوند تا مقادیر آن بردار اصلاح و عوض شود.
[](https://en.wikipedia.org/wiki/Matrix)
در GLSL پشتیبانی از ماتریس های 2و 3 و 4 بعدی تعبیه شده است: `mat2` (2x2), `mat3` (3x3) and `mat4` (4x4). همچنین GLSL از ضرب ماتریس ها پشتیبانی میکند (`*`) همچنین از تابع خاص (`matrixCompMult()`).
بر اساس رفتار ماتریس ها، میتوانیم ماتریس هایی تولید کنیم که رفتار خاصی انجام میدهند، مثلا از ماتریس میتوان برای حرکت یک بردار استفاده کرد :
جالب تر اینکه از یک ماتریس برای چرخش سیستم مختصات میتوانیم استفاده کنیم:
به تابعی که چرخش دو بعدی را انجام میدهد نگاهی بیاندازید، این تابع برای چرخش مختصات حول نقطه 0و0 از فرمول فوق پیروی میکند formula .
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}با توجه که روشی که در رسم اشکال داشتیم، این دقیقا آن چیزی نیست که میخواهیم. شکل ضربدری ما در مرکز کنوس کشیده شده است، پس قبل از چرخواندن فضا باید شکل را از مرکز به نقظه 0و0 انتقال دهیم، فضا را بچرخوانیم، سپس در نهایت شکل را به مکان اصلی آن برگردانیم.
مانند کد زیر:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
تمرین های زیر را انجام دهید:
* خط 45 را از حالت کامنت خارج کنید و ببینید چه اتفاقی در حال رخ دادن است.
* انتقال در خط های 37 و 39 را قبل و بعد از چرخش را کامنت کنید و ببینید چه اتفاقی میافتد.
* برای بهبود انیمیشن در در تمرین انتقال، از چرخش استفاده کنید.
اندازه
دیدم که چگونه ماتریس ها کمک به جابه جایی و چرخش در فضا کمک میکنند. (یا به طور دقیق تر، انتقال سیستم مختصات برای چرخش و انتقال) اگر با نرم افزار های سه بعدی یا توابع push و pop در نزم افزار processing کار کرده باشید، میدانید که ماتریس ها برای تغییر اندازه اشکال هم استفاده میشوند.
طبق فرمول قبلی میتوان نحوه ساخت ماتریس مقیاس دو بعدی را فهمید:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
تمرینات زیر را انجام دهید تا عمیق تر نحوه عملکرد آن را متوجه شوید..
* خط 42 را از کامنت خارج کنید و تغییرات فضای مختصات را مشاهده کنید.
* کد های انتقال در خطوط 37 و 39 را کامنت کنید و ببینید چه اتفاقی رخ میدهد.
* سعی کنید ماتریس انتقال را با ماتریس اندازه(مقیاس) ترکیب کنید، توجه داشته باشید که ترتیب مهم است. ابتدا ماتریس ها را با هم ضرب کنید و سپس آنان در بردار مختصات ضرب کنید.
* حالا که نحوه کشیدن و انتقال و چرخش شکل های مختلف را میدانید، وقت آن است که یک شیدر ترکیبی با آن ها بسازید. یک UI مثل fake UI or HUD (heads up display) بسازید. از این مثال داخل shader toy هم میتوانید ایده و انگیزه بگیرید Ndel.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
سایر کاربرد های ماتریس: YUV رنگ
YUV یک فضای رنگی است، که برای رمز گذاری آنالوگ عکس و فیلم است، و برای کاهش پهنای باند رنگی از دامنه درک انسان استفاده میکند.
کد زیر این عملیات ماتریسی را در GLSL برا تبدیل رنگ ها از حالتی به حالت دیگر نشان میدهد.
<div class="codeAndCanvas" data="yuv.frag"></div>
همانطور که میبینید با رنگ ها مانند بردار رفتار میکنیم و آنان را در هم ضرب میکنیم به این صورت ما مقادیر را به اطراف منتقل میکنیم.
در این قسمت یاد گرفتیم، چگونه با استفاده از ماتریس ها انتقال، تغییر مقیاس و چرخش انجام دهید. این انتقال ها برای ساخت ترکیب هایی از شکل های که قبلا آموختیم ضروری خواهند بود. در فصل بعد از آنچه تا کنون آموختیم برای ساخت پترن های رویه ای procedural patterns استفاده خواهیم کرد، و متوجه خواهید شد تکرار و تغییر کد نویسی میتواند یک تمرین هیجان انگیز باشد.
---
08/README Fr (08/README-fr.md)
Matrices 2D
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Translation
Au chapitre précédent, nous avons vu comment créer des formes, l'astuce pour déplacer une forme consistait à déplacer le système de coordonnées lui-même.
Nous opérions cette translation en ajoutant un vecteur 2d à la variable `st` contenant la position du fragment ce qui avait pour conséquence de décaler la position de l'ensemble des fragments en cours de traitement.
Un exemple sera sans doute plus explicite :
* Décommentez la ligne 35 du code ci-dessous pour voir le système de coordonnées bouger.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Essayez l'exercice suivant :
* Servez vous d'`u_time` et des fonctions de formes pour déplacer la croix de façon intéressante.
Pensez à un type de mouvement et tentez de l'appliquer à la croix. Prenez des exemples de la "vraie vie", ça aide! Par exemple, un mouvement de vagues, un pendule, une balle qui rebondit, un vélo qui freine, etc.
Rotations
Pour effectuer une rotation, nous devons transformer tout le système de coordonnées.
Pour nous aider, nous allons utiliser un objet matrix qui permet de manipuler une matrice de transformation 2D.
Une matrice est un objet organisé en 'grille', elle se compose de colonnes et de rangées de chiffres.
Lorsqu'on multiplie un vecteur par une matrice, la matrice exécute une série d'opérations, dans un certain ordre et transforme le vecteur en fonction des valeurs qu'elle contient.
C'est pourquoi on les appelle souvent des matrices de transformations, la plupart des APIs graphiques - tout support confondu - proposent une matrice de transformation 2D, 3D ou 4D.
Les matrices permettent de stocker plusieurs types de transformations (le plus souvent : translation, rotation et échelle) dans un objet compact et utilisable sur des vecteurs.
Pour en savoir plus, référez vous à l'article Wikipédia sur les matrices: [](https://fr.wikipedia.org/wiki/Matrice_(math%C3%A9matiques))
Le GLSL supporte nativement les matrices à deux, trois et quatre dimensions: `mat2` (2x2), `mat3` (3x3) et `mat4` (4x4).
Le GLSL surcharge certains opérateurs, notamment la multiplication et offre des fonctions spécifiques comme `matrixCompMult()`, qui effectue un produit scalaire des composants des matrices passées en argument.
Les matrices sont très utiles et permettent d'obtenir des transformations et des comportements spécifiques.
Par exemple, on peut utiliser une matrice pour appliquer une translation à un vecteur :
La matrice est à gauche, les valeurs de translation tx et ty (la quantité de déplacement en x et en y) sont stockées à un endroit précis de la matrice (troisième colonne, rangées 1 et 2). le vecteur 2D que nous voulons transformer est au centre, le point entre les deux représente une multiplication et le résultat est à droite.
Une matrice permet également d'effectuer une rotation :
Notez que les valeurs de rotations (cos() et sin()) sont décrites à un endroit (colonnes, rangées) différent des valeurs de translation (tx et ty).
C'est ce qui permet de stocker plusieurs transformations dans la même matrice.
Le code suivant montre comment construire une matrice de rotation 2D.
Cette fonction s'appuie sur la formule suivante pour faire pivoter un vecteur 2D autour de l'origine du système de coordonnées : le point `vec2(0.0)`.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}Cette manière d'effectuer une rotation (autour de l'origine) ne marche pas avec l'approche que nous avons eu jusqu'à présent pour dessiner les formes.
En effet notre croix est dessinée au centre du canvas (le point `vec2(0.5)`) alors que la rotation se fait autour de l'origine (le point `vec2(0.0)`).centre
Donc, avant d'effectuer la rotation, nous devons déplacer la forme du vers l'origine `vec2(0.0)`, appliquer la rotation puis re-déplacer la forme au centre du canvas.
Le code ressemble à ça:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Essayez les choses suivantes :
* Décommentez la ligne 45 du code ci-dessus et observez ce qui se passse.
* Commentez les translations avant et après la rotation aux lignes 37 et 39, observez ce qui se passe.
* Utilisez les rotations pour améliorer l'animation basée sur les translations que vous avez fait plus haut.
Echelle
Nous avons vu que les matrices permettent de déplacer et de faire pivoter les objets dans l'espace ou plus exactement de déplacer l'origine et de lui appliquer une rotation avant de dessiner une forme.
Si vous avez déjà fait de la 3D ou que vous vous êtes déjà servi des méthodes push et pop des matrices dans Processing, vous savez qu'on peut également se servir de matrices pour redimensionner un objet.
Voici à quoi ressemble une matrice 2D de mise à l'échelle:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Pour mieux comprendre comment ça marche, essayez les choses suivantes :
* Décommentez la ligne 42 du code ci-dessus pour visualiser la mise à l'échelle en couleur.
* Regardez ce qui se passe quand vous commentez les translations avant et après la mise à l'échelle aux lignes 37 et 39.
* Essayez de combiner rotation et mise à l'échelle dans une seule matrice. Attention, l'ordre des opérations est important ; multipliez d'abord les matrices entre elles avant de multiplier le vecteur par la matrice finale.
* A présent que vous savez dessiner des formes et les transformer, essayez de faire une petite composition.
Reprenez le design d'une de ces fausses UI et HUD (Heads Up Display).
Servez vous du sketch ShaderToy suivant (par Ndel) comme point de départ et/ou comme référence.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Autres utilisations des matrices: l'espace colorimétrique YUV
YUV est un espace colorimétrique utilisé pour l'encodage de photographies et de vidéos analogues.
Il prend en compte ce que perçoit l'oeil humain de la lumière et modifie les plages du spectre lumineux pour s'y adapter.
Dans le code suivant, nous utilisons une matrice pour opérer la conversion d'un espace colorimétrique à l'autre (RGB est l'espace colorimétrique par défaut)
<div class="codeAndCanvas" data="yuv.frag"></div>
Comme vous pouvez le voir, nous traitons les couleurs comme des vecteurs et les multiplions par une matrice.
Nous avons déplacé les valeurs de la couleur comme nous aurions déplacé une vecteur de position dans l'espace.
Dans ce chapitre, nous avons vu comment utiliser une matrice pour déplacer, faire pivoter et redimensionner un vecteur.
Ces transformations sont essentielles lorsqu'on veut agencer les formes que nous avons appris à créer au chapitre précédent.
Au chapitre suivant nous allons utiliser ce que nous venons de voir pour créer des motifs procéduraux.
Vous verrez que créer des répétitions et des variations peut être très gratifiant.
---
08/README Id (08/README-id.md)
Matriks 2D
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Translasi
Pada bab sebelumnya kita telah mempelajari cara membuat beberapa bentuk - trik untuk memindahkan bentuk tersebut adalah dengan memindahkan sistem koordinat itu sendiri. Kita bisa mencapainya hanya dengan menambahkan vektor ke variabel `st` yang berisi lokasi setiap fragmen. Ini menyebabkan seluruh sistem koordinat ruang bergerak.
Ini lebih mudah dilihat daripada dijelaskan, jadi lihat sendiri:
* Hapus komentar baris 35 dari kode di bawah ini untuk melihat bagaimana ruang itu sendiri bergerak.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Sekarang coba latihan berikut:
* Menggunakan `u_time` bersama dengan fungsi pembentuk memindahkan tanda silang kecil dengan cara yang menarik. Telusuri kualitas gerakan tertentu yang Anda minati dan cobalah melakukan gerakan silang dengan cara yang sama. Merekam sesuatu dari "dunia nyata" terlebih dahulu mungkin berguna - bisa saja gelombang datang dan pergi, gerakan pendulum, bola yang memantul, mobil yang sedang melaju, sepeda yang berhenti.
Rotasi
Untuk memutar objek kita juga perlu memindahkan seluruh sistem ruang. Untuk itu kita akan menggunakan [matriks] (http://en.wikipedia.org/wiki/Matrix_%28mathematics%29). Matriks adalah kumpulan angka yang terorganisir dalam kolom dan baris. Vektor dikalikan dengan matriks mengikuti sekumpulan aturan yang tepat untuk mengubah nilai vektor dengan cara tertentu.
[](https://en.wikipedia.org/wiki/Matrix)
GLSL memiliki dukungan native untuk dua, tiga, dan empat dimensi matriks: `mat2` (2x2), `mat3` (3x3) dan ` mat4` (4x4). GLSL juga mendukung perkalian matriks (`*`) dan fungsi spesifik matriks (`matrixCompMult()`).
Berdasarkan bagaimana matriks berperilaku, dimungkinkan untuk membangun matriks untuk menghasilkan perilaku tertentu. Misalnya kita dapat menggunakan matriks untuk menerjemahkan vektor:
Menariknya, kita bisa menggunakan matriks untuk memutar sistem koordinat:
Perhatikan kode berikut untuk fungsi yang menyusun matriks rotasi 2D. Fungsi ini mengikuti [rumus] (http://en.wikipedia.org/wiki/Rotation_matrix) di atas untuk vektor dua dimensi untuk memutar koordinat di sekitar titik `vec2(0.0)`.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}Menurut cara kita menggambar bentuk, ini bukanlah yang kita inginkan. Bentuk silang kami digambar di tengah kanvas yang sesuai dengan posisi `vec2(0,5)`. Jadi, sebelum kita memutar ruang kita perlu memindahkan bentuk dari center ke koordinat `vec2(0.0)`, putar spasi, lalu pindahkan kembali ke tempat semula.
Itu terlihat seperti kode berikut:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Coba latihan berikut:
* Hapus tanda komentar baris 45 kode diatas dan perhatikan apa yang terjadi.
* Komentari terjemahan sebelum dan sesudah rotasi, pada baris 37 dan 39, dan amati konsekuensinya.
* Gunakan rotasi untuk meningkatkan animasi yang Anda simulasi dalam latihan penerjemahan.
Skala
Kami telah melihat bagaimana matriks digunakan untuk menerjemahkan dan memutar objek di ruang angkasa. (Atau lebih tepatnya untuk mentransformasikan sistem koordinat untuk memutar dan memindahkan objek.) Jika Anda telah menggunakan perangkat lunak pemodelan 3D atau fungsi matriks push dan pop dalam Pemrosesan, Anda akan tahu bahwa matriks juga dapat digunakan untuk menskalakan ukuran sebuah obyek.
Mengikuti rumus sebelumnya, kita bisa mengetahui cara membuat matriks penskalaan 2D:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Cobalah latihan berikut untuk memahami lebih dalam cara kerjanya.
* Hapus komentar baris 42 kode di atas untuk melihat koordinat spasi sedang diskalakan.
* Lihat apa yang terjadi jika Anda mengomentari terjemahan sebelum dan sesudah penskalaan pada baris 37 dan 39.
* Coba gabungkan matriks rotasi bersama dengan matriks skala. Ketahuilah bahwa urutan itu penting. Kalikan dengan matriks terlebih dahulu lalu kalikan vektornya.
* Sekarang setelah Anda tahu cara menggambar berbagai bentuk, dan memindahkan, memutar, dan menskalakannya, sekarang saatnya membuat komposisi yang bagus. Mendesain dan membuat UI atau HUD palsu (tampilan kepala ke atas). Gunakan contoh ShaderToy berikut oleh Ndel untuk inspirasi dan referensi.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Kegunaan lain untuk matriks: Warna YUV
YUV adalah ruang warna yang digunakan untuk pengkodean analog foto dan video yang memperhitungkan jangkauan persepsi manusia untuk mengurangi bandwidth komponen chrominance.
Kode berikut adalah kesempatan menarik untuk menggunakan operasi matriks di GLSL untuk mengubah warna dari satu mode ke mode lainnya.
<div class="codeAndCanvas" data="yuv.frag"></div>
Seperti yang Anda lihat, kami memperlakukan warna sebagai vektor dengan mengalikannya dengan matriks. Dengan cara itu kita "memindahkan" nilai-nilai itu.
Dalam bab ini kita telah mempelajari bagaimana menggunakan transformasi matriks untuk memindahkan, memutar dan menskalakan vektor. Transformasi ini akan sangat penting untuk membuat komposisi dari bentuk yang telah kita pelajari di bab sebelumnya. Di bab selanjutnya kita akan menerapkan semua yang telah kita pelajari untuk membuat pola prosedural yang indah. Anda akan menemukan bahwa pengulangan dan variasi pengkodean bisa menjadi praktik yang menarik.
---
08/README It (08/README-it.md)
Matrici 2D
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Traslare
Nel capitolo precedente abbiamo imparato a fare alcune figure - il trucco per spostare quelle figure è stato di spostare il sistema di coordinate stesso. Siamo in grado d'ottenere questa trasformazione aggiungendo un vettore alla variabile `st` che contiene la posizione di ogni frammento. Ciò causa uno spostamento complessivo del sistema di coordinate.
Di sicuro è più facile da vedere che da spiegare:
* Rimuovete il commento alla linea 35 del codice sottostante e osservate come il sistema di coordinate si muove.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Ora provate il seguente esercizio:
* Utilizzate `u_time` insieme alle funzioni di forma per spostare la piccola croce in un modo interessante. Pensate a un tipo di movimento e tentate di applicarlo alla croce. Prendete degli esempi dal "mondo reale", potrebbero esservi utili - per esempio l'andirivieni delle onde, un movimento a pendolo, una palla che rimbalza, una macchina che accelera, una bicicletta che si ferma.
Rotazioni
Anche per ruotare degli oggetti abbiamo bisogno di spostare l'intero sistema spaziale. Per questo utilizzeremo una matrice. Una matrice è un insieme di numeri organizzato in colonne e righe. Quando si moltiplica un vettore per una matrice, la matrice esegue una serie di operazioni e trasforma il vettore in funzione dei valori che questa contiene.
[](https://it.wikipedia.org/wiki/Matrice)
GLSL ha un supporto nativo per le matrici a due, tre e quattro dimensioni: `mat2` (2x2), `mat3` (3x3) e `mat4` (4x4). GLSL supporta anche la moltiplicazione di matrici (`*`) e una funzione specifica chiamata (`matrixCompMult()`).
Le matrici possono essere usate per produrre dei comportamenti specifici. Per esempio per traslare un vettore:
Ancora più interessante è la possibilità di utilizzare una matrice per ruotare il sistema di coordinate:
Il seguente codice mostra come costruire una matrice di rotazione 2D. Questa funzione segue la formula per vettori a due dimensioni e fa ruotare le coordinate intorno al punto `vec2(0.0)`.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}Questa maniera d'effettuare una rotazione (intorno all'origine) non funziona con l'approccio che abbiamo utilizzato finora per disegnare le figure. La nostra croce viene disegnata al centro della tela, che corrisponde alla posizione `vec2(0.5)`. Quindi, prima di ruotare lo spazio, abbiamo bisogno di spostare la figura dal centro alla coordinata `vec2(0.0)`, poi di ruotare lo spazio, e infine di spostarla di nuovo nella posizione originale.
Date un'occhiata al codice:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Provate i seguenti esercizi:
* Rimuovete il commento alla linea 45 del codice qui sopra e prestate attenzione a ciò che accade.
* Commentate le traslazioni, prima e dopo la rotazione, alle linee 37 e 39, e osservate le conseguenze.
* Utilizzate delle rotazioni per migliorare l'animazione che avete simulato nell'esercizio sulla traslazione.
Scalare
Abbiamo visto come le matrici sono usate per traslare e ruotare gli oggetti nello spazio. (O più precisamente come trasformare il sistema di coordinate per ruotare e spostare gli oggetti.) Se avete utilizzato un software di modellazione 3D o le funzioni push e pop delle matrici in Processing, si sa che le matrici possono essere utilizzati anche per scalare la dimensione di un oggetto.
Usando la formula precedente, siamo in grado di scrivere una matrice a due dimensioni per scalare una figura:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Provate i seguenti esercizi per capirne il funzionamento.
* Rimuovete il commento alla linea 42 del codice per vedere come funziona la messa in scala attraverso l'uso di colori.
* Osservate cosa succede quando si commentano le traslazioni, prima e dopo il ridimensionamento alle linee 37 e 39.
* Provate a combinare una matrice di rotazione insieme con una matrice di scala. Attenzione, l'ordine delle operazioni è importante: per prima cosa moltiplicate le matrici fra di loro e poi moltiplicate la matrice finale per i vettori.
* Ora che sapete come disegnare, spostare, ruotare, scalare molteplici forme, è il momento di fare una bella composizione. Progettate e realizzate una falsa UI o HUD (heads up display). Utilizzate il seguente esempio su ShaderToy di Ndel per avere un riferimento.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Altri usi delle matrici: il colore YUV
Lo YUV è uno spazio colore utilizzato per la codifica analogica di foto e video che tenga conto della gamma di percezione umana per ridurre la larghezza di banda dei componenti della crominanza.
Nel seguente codice utilizzeremo delle operazioni di matrice in GLSL per trasformare i colori da uno spazio colore all'altro.
<div class="codeAndCanvas" data="yuv.frag"></div>
Come si può vedere, stiamo trattando i colori come dei vettori che vengono moltiplicati con delle matrici. Abbiamo "spostato" i valori di colore come se avessimo spostato un vettore di posizione nello spazio.
In questo capitolo abbiamo imparato come usare le trasformazioni di matrice per spostare, ruotare e ridimensionare i vettori. Queste trasformazioni saranno essenziali per creare delle composizioni con le figure che abbiamo visto nel capitolo precedente. Nel prossimo capitolo provvederemo ad applicare tutto quello che abbiamo imparato per fare dei bei pattern procedurali. Vedrete come la ripetizione e la variazione del codice possano diventare delle pratiche gratificanti.
---
08/README Pl (08/README-pl.md)
Macierze 2D
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Translacja
W poprzednim rozdziale dowiedzieliśmy się, jak tworzyć różne kształty. Przesuwanie tych kształtów polega na przesuwaniu samego układu współrzędnych. Możemy to osiągnąć poprzez proste dodanie wektora do zmiennej st, zawierającej położenie każdego fragmentu. Powoduje to przesunięcie całego układu współrzędnych.
Łatwiej jest to zobaczyć niż wytłumaczyć, zatem:
*Odkomentuj linijkę 35 poniższego kodu, by zobaczyć jak przestrzeń się przesuwa.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Spróbuj teraz wykonać następujące ćwiczenie:
* Używając u_time wraz z shaping functions poruszaj małym krzyżem w ciekawy sposób. Poszukaj interesującego cię ruchu i spróbuj sprawić, by krzyż poruszał się w ten sam sposób. Przydatne może być nagranie najpierw czegoś z "prawdziwego świata" - może to być przypływ i odpływ fal, ruch wahadła, odbijająca się piłka, przyspieszający samochód, zatrzymujący się rower.
Rotacja
Aby obracać obiekty również musimy poruszać całym układem przestrzennym. Do tego celu będziemy używać macierzy. Macierz to uporządkowany zbiór liczb w kolumnach i wierszach. Wektory są mnożone przez macierze według ściśle określonych reguł w celu zmodyfikowania wartości wektora w określony sposób.
[](https://en.wikipedia.org/wiki/Matrix)
GLSL posiada natywne wsparcie dla dwu, trzy i czterowymiarowych macierzy: mat2 (2x2), mat3 (3x3) i mat4 (4x4). GLSL obsługuje również mnożenie macierzy (*) oraz specyficzną dla macierzy funkcję matrixCompMult().
Na podstawie tego, jak zachowują się macierze, możliwe jest skonstruowanie macierzy w celu wytworzenia określonych zachowań. Na przykład możemy użyć macierzy do translacji wektora:
Co ciekawsze, możemy użyć macierzy do obrócenia całego układu współrzędnych
Spójrz na poniższy kod funkcji, która konstruuje dwuwymiarową macierz rotacji. Funkcja ta oparta jest na wzorze dla dwuwymiarowych wektorów, aby obrócić współrzędne wokół punktu vec2(0,0).
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}Zgodnie ze sposobem, w jaki rysowaliśmy kształty, nie jest to dokładnie to, czego chcemy. Nasz krzyż jest rysowany w centrum płótna, co odpowiada pozycji vec2(0.5). Tak więc, zanim obrócimy przestrzeń musimy przenieść ten krzyż z centrum na współrzędną vec2(0.0), obrócić przestrzeń, a następnie ostatecznie przenieść go z powrotem na pierwotne miejsce.
Co odpowiada ponizszemu kodowi:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Spróbuj wykonać następujące ćwiczenia:
* Odkomentuj linię 45 powyższego kodu i zwróć uwagę na to, co się stanie.
* Zakomentuj translacje przed i po rotacji, w liniach 37 i 39, i zaobserwuj konsekwencje.
* Użyj rotacji, aby poprawić ruch, który zasymulowałeś w ćwiczeniu z podrozdziału "Translcja".
Skalowanie
Widzieliśmy już, jak macierze służą do translacji i rotacji obiektów w przestrzeni. (A dokładniej do przekształcania układu współrzędnych w celu obracania i przesuwania obiektów). Jeśli używałeś programów do modelowania 3D albo funkcji macierzowych push i pop w Processing, to pewnie wiesz, że macierze mogą być również używane do skalowania rozmiaru obiektu.
Na podstawie powyższego wzoru, możemy stworzyć 2D macierz skalowania w GLSL:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Spróbuj następujących ćwiczeń, aby głębiej zrozumieć, jak to działa.
* Odkomentuj linię 42 powyższego kodu, aby zobaczyć skalowanie współrzędnych przestrzeni.
* Zobacz, co się stanie, gdy zakomentujesz translacje przed i po skalowaniu w liniach 37 i 39.
* Spróbuj połączyć macierz rotacji wraz z macierzą skalowania. Bądź świadomy, że kolejność ma znaczenie. Najpierw pomnóż przez macierz, a potem pomnóż wektory.
* Teraz, gdy wiesz już, jak rysować różne kształty oraz przesuwać, obracać i skalować je, czas na stworzenie ładnej kompozycji. Zaprojektuj i skonstruuj UI lub HUD (ang. "heads up display"). Użyj następującego przykładu ShaderToy autorstwa Ndel jako inspiracji.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Inne zastosowania macierzy: Kolor YUV
YUV to przestrzeń barw stosowana do analogowego kodowania zdjęć i filmów, która uwzględnia ludzką percepcję w celu zmniejszenia redundantnych informacji zawartych w reprezentacji RGB.
Poniższy kod jest ciekawą okazją do wykorzystania operacji macierzowych w GLSL do transformacji kolorów z jednej przestrzeni do drugiej.
<div class="codeAndCanvas" data="yuv.frag"></div>
Jak widać traktujemy kolory jak wektory, które można mnożyć przez macierze. W ten sposób mapujemy wartości.
W tym rozdziale dowiedzieliśmy się, jak używać przekształceń macierzowych do przesuwania, obracania i skalowania wektorów. Przekształcenia te będą niezbędne do tworzenia kompozycji z kształtów, które poznaliśmy w poprzednim rozdziale. W następnym rozdziale zastosujemy wszystko, czego dotychczas się nauczyliśmy, do tworzenia pięknych proceduralnych wzorów. Zobaczysz, że programowanie powtórzeń i wariacji może być bardzo ekscytujące.
---
08/README Pt (08/README-pt.md)
Matrizes 2D
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Translação
No capítulo anterior, aprendemos como fazer algumas formas básicas - o macete para mover essas formas é mover o próprio sistema de coordenadas. Podemos fazer isso simplesmente adicionando um vetor à variável `st` que contém a localização de cada fragmento. Isso faz com que todo o sistema de coordenadas espaciais se mova.
Isso é mais fácil de ver do que explicar, então, veja por você mesmo:
* Descomente a linha 35 do código acima para ver como o espaço se move.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Agora, tente o seguinte exercício:
* Usar `u_time` junto com funções de forma, mova a pequena cruz de uma forma interessante. Procure por uma qualidade específica de movimento que você esteja interessado e tente fazer a cruz se mover do mesmo jeito. Gravar algo do "mundo real" primeiro pode ser útil - poderia ser o ir e vir das ondas, um movimento de um pêndulo, uma bola saltitante, um carro acelerando, uma bicicleta parando.
Rotações
Para rotacionar objetos, também precisamos mover o sistema espacial inteiro. Para isso, vamos usar uma matriz. Uma matriz é um conjunto organizado de números em colunas e linhas. Vetores são multiplicados por matrizes seguindo um conjunto preciso de regras de forma a modificar os valores do vetor de maneira particular.
[](https://en.wikipedia.org/wiki/Matrix)
GLSL tem suporte nativo para matrizes de duas, três e quatro dimensões: `mat2` (2x2), `mat3` (3x3) and `mat4` (4x4). GLSL também suporta multiplicação de matrizes (`*`) e uma função específica de matrizes (`matrixCompMult()`).
Baseado em como as matrizes se comportam, é possível construir matrizes para produzir comportamentos específicos. Por exemplo, podemos usar uma matriz para transladar um vetor:
Mais interessante ainda: podemos usar uma matriz para rotacionar o sistema de coordenadas:
Dê uma olhada no código a seguir para ver uma função que constrói uma matriz de rotação 2D. Essa função segue a fórmula acima para vetores de duas dimensões, para rotacionar as coordenadas em torno do ponto `vec2(0.0)`.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}De acordo com a forma como estamos desanhando nossas formas, não é isso, exatamente, o que queremos. Nossa forma de cruz é desenhada no meio da tela, o que corresponde à posição `vec2(0.5)`. Então, antes de rodarmos o espaço, precisamos mover a forma do center para a coordenada `vec2(0.0)`, rotacionar o espaço, e finalmente mover de volta para o lugar original.
Isso se parece com o seguinte código:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Tente os exersícios a seguir:
* Descomente a linha 45 do código acima, e preste atenção no que acontece.
* Comente as translações antes e depois da rotação, nas linhas 37 e 39, e observe as consequências.
* Use rotações para melhorar a animação que você simulou no exercício de translação.
Escala (scale)
Vimos como as matrizes são usadas para transladar e rotacionar objetos no espaço (ou, mais precisamente, transformar o sistema de coordenadas para rotacionar e mover objetos). Se você já usou softwares de modelagem 3D ou as funções de push e pop no Processing, já sabe que as matrizes também podem ser usadas para escalar o tamanho de um objeto.
Seguindo a fórmula anterior, você pode calcular como fazer uma matriz para escalar em 2D:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Tente os seguintes exersícios para entender mais a fundo como isso funciona.
* Descomente a linha 42 do código acima, para ver a coordenada de espaço sendo escalada.
* Veja o que acontece quando você comenta as translações antes e depois de escalar, nas linhas 37 e 39.
* Tente combinar uma matriz de rotação com uma de escala. Tenha em mente que a ordem faz diferença. Multiplique pela matriz primeiro e então multiplique os vetores.
* Agora que você sabe como desenhar formas diferentes, e mover, rotacionar e escalá-las, é hora de fazer uma composição bem maneira. Projete e construa um HUD (heads up display) ou UI falso. Use o seguinte ShaderToy de exemplo, do Ndel como inspiração e referência.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Outros usos para matrizes: cores YUV
YUV é um espaço de cores usado para codificação analógica de fotos e vídeos, que considera o faixa da percepção humana para reduzir a largura de banda dos componentes de crominância.
O código a seguir é uma oportunidade interessante para usar operações de matrizes em GLSL para transformar as cores de um modo para outro.
<div class="codeAndCanvas" data="yuv.frag"></div>
Como você pode ver, estamos tratando as cores como vetores, multiplicando-os com matrizes. Dessa forma, nós "movemos" os valores.
Neste capítulo, aprendemos como usar transformações de matrizes para mover, rotacionar e escalar vetores. Essas transformações serão essenciais para fazer composições com as formas que aprendemos no capítulo anterior. No próximo capítulo, vamos aplicar tudo o que aprendemos para fazer padrões procedurais bem bonitos. Você vai achar que a repetição e variação na programação podem ser uma prática excitante.
---
08/README Ru (08/README-ru.md)
Двумерные матрицы
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Перенос
В предыдущей главе мы научились рисовать фигуры. А для того, чтобы передвинуть фигуру, достаточно передвинуть саму систему координат. Этого можно добиться всего лишь прибавление вектора к переменной `st`, содержащей положение фрагмента. Это приводит к перемещению всей системы координат в пространстве.
Это проще увидеть, чем объяснить, поэтому смотрите сами:
* Раскомментируйте строку 35 в коде ниже чтобы увидеть как движется само пространство.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Теперь попробуйте выполнить следующее:
* Используйте `u_time` и функции формы для перемещения креста каким-нибудь нестандартным способом. Придумайте движение, которое кажется вам интересным, и заставьте крест двигаться таким образом. Попробуйте срисовать что-нибудь из реального мира, например прибегающие и отступающие волны, движение маятника, подпрыгивающий мячик, ускоряющуюся машину или тормозящий велосипед.
Повороты
Для вращения объектов нам снова придётся поворачивать всё пространство. Для этого мы воспользуемся матрицей). Матрица - упорядоченный в строки и столбцы набор чисел. Векторы умножаются на матрицы по определённым правилам, изменяя значения вектора заданным способом.
[](https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%82%D1%80%D0%B8%D1%86%D0%B0_(%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0))
В GLSL есть встроенная поддержка двух- трёх- и четырёхмерных матриц: `mat2` (2x2), `mat3` (3x3) и `mat4` (4x4). GLSL так же поддерживает умножение матриц (`*`) и некоторые специальные функции (`matrixCompMult()`).
Мы можем конструировать матрицы, приводящие к определённому поведению. Например, можно использовать матрицу для переноса вектора:
Что более интересно, мы можем использовать матрицу для поворота системы координат:
Взгляните на код функции, которая конструирует двумерную матрицу поворота. Эта функция повторяет приведённую выше формулу для вращения вектора вокруг точки `vec2(0.0)`.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}Это не совсем то что нам нужно, если мы вспомним наш способ пострения геометрических фигур. Фигура креста нарисована в центре изображения, то есть в точке `vec2(0.5)`. Поэтому, прежде чем поворачивать пространство, нам нужно передвинуть фигуру из центра в точку `vec2(0.0)`, и только после этого повернуть пространство, и в конце не забыть передвинуть фигуру в исходное положение.
Рассмотрите код:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Попробуйте выполнить следующее:
* Раскомментируйте строку 45 и посмотрите что происходит.
* Закомментируйте сдвиги до и после поворота в строках 37 и 39, понаблюдайте за последствиями.
* Используйте вращение, чтобы улучшить анимацию, которую вы сделали в упражнении про перенос.
Масштаб
Мы увидели как можно использовать матрицы для переноса и поворота объектов в пространстве. Или, точнее, как трансформировать систему координат для вращения и движения объектов. Если вы пользовались софтом для 3D-моделирования или функциями push и pop для матриц в Processing, вы скорее всего знаете, что с помощью матриц можно ещё и масштабировать объекты.
Следуя этой формуле, можно сконструировать двумерную матрицу масштабирования.
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Выполните следующие упражнения для более глубокого понимания того, как это работает.
* Раскомментируйте строку 42 в коде выше и пронаблюдайте масштабирование пространственных координат.
* Посмотрите что произойдёт, если закомментировать переносы в строках 37 и 39.
* Попробуйте скомбинировать матрицы масштабирования и поворота. Помните, что порядок важен. Сначала перемножьте матрицы, а затем умножайте результат на векторы.
* Теперь когда вы умеете рисовать различные фигуры и управлять их положением в пространстве, мы можем приступить к композиции. придумайте и нарисуйте бутафорский интерфейс пользователя или HUD (heads up display, то есть когда информация проецируется на стекло шлема или транспортного средства). Для поиска идей используйте следующий пример с ShaderToy, написанный пользователем Ndel.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Другие применения матриц: цвет в пространстве YUV
YUV - цветовое пространство для аналогового кодирования фото и видео, разработанное с учётом особенностей восприятия человека чтобы снизить требования к каналу передачи компонентов цвета.
В следующем коде матричные операции GLSL используются весьма интересно - с их помощью сделано преобразование из одного цветового пространства в другое.
<div class="codeAndCanvas" data="yuv.frag"></div>
Здесь мы трактуем цвета как векторы и умножаем их на матрицы. Таким образом, мы «перемещаем» значения цвета.
В этой главе мы научились использовать матричные преобразования для сдвига, поворота и масштабирования векторов. Эти трансформации очень важны при построении композиций из фигур, которые мы рисовали в предыдущей главе. А в следующей главе мы используем все полученные знания для создания красивых процедурных узоров. Вы увидите, что программирование повторений и изменений может быть захватывающим занятием.
---
08/README Tr (08/README-tr.md)
2B Matrisler
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Öteleme (Translate)
Önceki bölümde bazı şekiller yapmayı öğrendik — bu şekilleri hareket ettirmenin püf noktası koordinat sisteminin kendisini hareket ettirmektir. Bunu, her bir fragmanın konumunu içeren `st` değişkenine basitçe bir vektör ekleyerek başarabiliriz. Bu, tüm uzay koordinat sisteminin hareket etmesine neden olur.
Bunu açıklamaktansa görmek daha kolaydır; kendiniz görmek için:
* Uzayın kendisinin nasıl hareket ettiğini görmek için aşağıdaki kodun 35. satırının yorumunu kaldırın.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Şimdi aşağıdaki alıştırmayı deneyin:
* `u_time` ile birlikte şekillendirme fonksiyonlarını kullanarak küçük artıyı ilginç bir şekilde hareket ettirin. İlgilendiğiniz belirli bir hareket kalitesi arayın ve artıyı aynı şekilde hareket ettirmeye çalışın. Önce "gerçek dünyadan" bir şey kaydetmek faydalı olabilir — dalgaların gelişi ve gidişi, bir sarkaç hareketi, zıplayan bir top, hızlanan bir araba, duran bir bisiklet gibi.
Döndürme
Nesneleri döndürmek için tüm uzay sistemini hareket ettirmemiz de gerekir. Bunun için bir matris kullanacağız. Matris, sütun ve satırlar halinde düzenlenmiş bir sayı kümesidir. Vektörler, vektörün değerlerini belirli bir şekilde değiştirmek için kesin kurallar izlenerek matrislerle çarpılır.
[](https://en.wikipedia.org/wiki/Matrix)
GLSL'de iki, üç ve dört boyutlu matrisler için yerleşik destek vardır: `mat2` (2x2), `mat3` (3x3) ve `mat4` (4x4). GLSL ayrıca matris çarpımını (`*`) ve matrise özgü fonksiyonu (`matrixCompMult()`) da destekler.
Matrislerin davranışlarına dayanarak, belirli davranışlar üretmek için matrisler oluşturulabilir. Örneğin bir vektörü ötelemek için matris kullanabiliriz:
Daha ilginç olarak, koordinat sistemini döndürmek için bir matris kullanabiliriz:
2B döndürme matrisi oluşturan bir fonksiyon için aşağıdaki koda bakın. Bu fonksiyon, koordinatları `vec2(0.0)` noktası etrafında döndürmek için iki boyutlu vektörler için yukarıdaki formülü izler.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}Şekilleri çizme şeklimize göre, bu tam olarak istediğimiz şey değildir. Artı şeklimiz `vec2(0.5)` konumuna karşılık gelen tuvalin merkezinde çizilmiştir. Bu yüzden, uzayı döndürmeden önce şekli merkezden `vec2(0.0)` koordinatına taşımamız, uzayı döndürmemiz ve sonunda orijinal yerine geri taşımamız gerekir.
Bu şöyle görünür:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Aşağıdaki alıştırmaları deneyin:
* Yukarıdaki kodun 45. satırının yorumunu kaldırın ve ne olduğuna dikkat edin.
* Döndürme öncesi ve sonrasındaki ötellemeleri (37 ve 39. satırlar) yorum satırı yapın ve sonuçları gözlemleyin.
* Öteleme alıştırmasında simüle ettiğiniz animasyonu iyileştirmek için döndürmeleri kullanın.
Ölçekleme
Matrislerin uzaydaki nesneleri ötelemek ve döndürmek için nasıl kullanıldığını gördük. (Daha doğrusu, nesneleri döndürmek ve taşımak için koordinat sistemini dönüştürmek.) 3D modelleme yazılımı veya Processing'deki push ve pop matris fonksiyonlarını kullandıysanız, matrislerin bir nesnenin boyutunu ölçeklemek için de kullanılabileceğini bilirsiniz.
Önceki formülü izleyerek, 2B ölçekleme matrisinin nasıl yapılacağını anlayabiliriz:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Bunun nasıl çalıştığını daha derinden anlamak için aşağıdaki alıştırmaları deneyin.
* Uzay koordinatının ölçeklendiğini görmek için yukarıdaki kodun 42. satırının yorumunu kaldırın.
* Ölçeklemeden önceki ve sonraki ötellemeleri (37 ve 39. satırlar) yorum satırı yaparsanız ne olduğunu görün.
* Bir döndürme matrisini bir ölçekleme matrisiyle birleştirmeyi deneyin. Sıranın önemli olduğuna dikkat edin. Önce matris ile çarpın, sonra vektörleri çarpın.
* Artık farklı şekilleri çizmeyi, taşımayı, döndürmeyi ve ölçeklemeyi bildiğinize göre, güzel bir kompozisyon yapma zamanı. Sahte bir UI veya HUD (heads up display) tasarlayın ve inşa edin. İlham ve referans için Ndel tarafından yapılan aşağıdaki ShaderToy örneğini kullanın.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Matrisler için diğer kullanımlar: YUV renk
YUV, fotoğraf ve videoların analog kodlaması için kullanılan ve krominans bileşenlerinin bant genişliğini azaltmak için insan algısının aralığını dikkate alan bir renk uzayıdır.
Aşağıdaki kod, renkleri bir moddan diğerine dönüştürmek için GLSL'de matris işlemlerini kullanmanın ilginç bir fırsatıdır.
<div class="codeAndCanvas" data="yuv.frag"></div>
Gördüğünüz gibi, renkleri matrislerle çarparak vektörler olarak ele alıyoruz. Bu şekilde değerleri "hareket ettiriyoruz".
Bu bölümde vektörleri taşımak, döndürmek ve ölçeklemek için matris dönüşümlerini nasıl kullanacağımızı öğrendik. Bu dönüşümler, önceki bölümde öğrendiğimiz şekillerden kompozisyonlar yapmak için gerekli olacaktır. Bir sonraki bölümde öğrendiğimiz her şeyi güzel prosedürel desenler yapmak için uygulayacağız. Tekrarlama ve çeşitleme kodlamanın heyecan verici bir pratik olabileceğini göreceksiniz.
#### Araç kutunuz için
* LYGIA'nın uzay fonksiyonları, GLSL'de uzayı manipüle etmek için yeniden kullanılabilir fonksiyonlar kümesidir. Matrisleri uzayı manipüle etmek için nasıl kullanacağınızı öğrenmek için harika bir kaynaktır. Yeniden kullanılabilirlik, performans ve esneklik için tasarlanmış çok ayrıntılı bir kütüphanedir. Ve herhangi bir projeye ve framework'e kolayca eklenebilir.
---
08/README Ua (08/README-ua.md)
Двомірні матриці
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Переміщення
У попередньому розділі ми навчилися створювати деякі фігури — хитрість переміщення цих фігур полягає в тому, щоб перемістити саму систему координат. Ми можемо досягти цього, просто додавши вектор до змінної `st`, яка містить розташування кожного фрагмента. Це спричиняє зміщення у просторі усієї системи координат.
Це легше побачити, ніж пояснити, тому дивіться самі:
* Розкоментуйте рядок 35 в коді нижче, щоб побачити, як рухається сам простір.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Тепер спробуйте наступне:
* Використовуючи змінну `u_time` разом із функціями формування, надайде хрестику рух у новому цікавому напрямку. Знайдіть конкретний варіант руху, яка вас цікавить, і спробуйте змусити хрест рухатися таким же чином. Спробуйте для початку щось із "реального світу" — це можуть бути хвилеподібні рухи, коливання маятника, м’яч, що підстрибує, прискорення як в автомобіля чи зупинка велосипеда.
Обертання
Щоб обертати об'єкти, нам також потрібно рухати всю систему простору. Для цього ми будемо використовувати матрицю. Матриця — це організований набір чисел у стовпцях і рядках. Вектори помножуються на матриці відповідно до певних правил, щоб змінити значення вектора визначеним чином.
[](https://en.wikipedia.org/wiki/Matrix)
GLSL має вбудовану підтримку дво-, три- та чотиривимірних матриць: `mat2` (2x2), `mat3` (3x3) та `mat4` (4x4). GLSL також підтримує множення матриць (`*`) та специфічну для матриці функцію (`matrixCompMult()`).
Можна побудувати різні матриці для отримання певної поведінки. Наприклад, ми можемо використати матрицю для переміщення вектора:
Що ще цікавіше, ми можемо використати матрицю для обертання системи координат:
Подивіться на наведений нижче код функції, яка створює двовимірну матрицю обертання. Ця функція відповідає наведеній вище формулі для обертання координат навколо точки `vec2(0.0)`.
mat2 rotate2d(float _angle) {
return mat2(
cos(_angle), -sin(_angle),
sin(_angle), cos(_angle)
);
}Судячи з того, як ми малювали фігури, це не зовсім те, що нам потрібно. Наша форма хреста намальована в центрі полотна, що відповідає позиції `vec2(0.5)`. Отже, перед тим, як обертати простір, нам потрібно перемістити фігуру із центру до координати `vec2(0.0)`, повернути простір, а потім, нарешті, перемістити його назад у початкове положення.
Це можна запрограмувати таким чином:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Спробуйте наступні вправи:
* Розкоментуйте рядок 45 із коду вище та зверніть увагу на те, що відбувається.
* Закоментуйте зміщення координат до та після повороту, у рядках 38 та 42, спостерігаючи за наслідками.
* Використайте обертання, щоб покращити анімацію, яку ви моделювали під час вправи з переміщенням.
Масштаб
Ми побачили, як матриці використовуються для переміщення та обертання об'єктів у просторі. А точніше, як змінювати системи координат для обертання та переміщення об'єктів. Якщо ви користувалися програмним забезпеченням для 3D-моделювання або функціями push та pop для матриць у Processing, то знаєте, що матриці також можна використовувати й для масштабування розміру об'єктів.
Дотримуючись попередньої формули, ми можемо створити двомірну матрицю масштабування:
mat2 scale(vec2 _scale) {
return mat2(
_scale.x, 0.0,
0.0, _scale.y
);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Спробуйте виконати наступні вправи, щоб глибше зрозуміти, як це працює.
* Розкомментуйте рядок 42 із коду вище, щоб побачити масштабування просторових координат.
* Подивіться, що відбудеться, коли ви закоментуєте переміщення координат до та після масштабування в рядках 37 та 39.
* Спробуйте поєднати матрицю обертання разом із матрицею масштабування. Майте на увазі, що порядок має значення. Спочатку помножте на матрицю, а потім результат помножте на вектори.
* Тепер, коли ви знаєте, як малювати різні фігури, переміщувати, обертати та масштабувати їх, настав час створити гарну композицію. Спроєктуйте та створіть фейковий UI або HUD. HUD (heads up display) — мається на увазі розташування інформації на прозорих поверхнях або дисплеях. Для натхнення та довідки, можете використати наступний ShaderToy-приклад від Ndel.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Інше використання матриць: колір у просторі YUV
YUV — це колірний простір, який використовується для аналогового кодування фотографій та відео, яке враховує діапазон людського сприйняття для зменшення пропускної здатності компонентів колірності.
Наступний код є цікавою можливістю використовувати матричні операції в GLSL для перетворення кольорів з одного простору в інший.
<div class="codeAndCanvas" data="yuv.frag"></div>
Як бачите, ми розглядаємо кольори як вектори, перемножуючи їх на матриці. Таким чином ми "переміщуємо" значення кольорів.
У цьому розділі ми навчилися використовувати матричні перетворення для переміщення, обертання та масштабування векторів. Ці трансформації будуть корисними для створення композицій із фігур, про які ми дізналися із попереднього розділу. У наступному розділі ми застосуємо всі здобуті знання для створення красивих процедурних візерунків. Ви побачите, що програмування повторень та варіацій може бути захопливою діяльністю.
---
08/README Vi (08/README-vi.md)
Ma trận 2 chiều
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
Chuyển động thẳng / tịnh tiến (Translate)
Ở chương trước ta đã học cách vẽ nhiều hình khác nhau. Để di chuyển tịnh tiến các hình đó, thay vì di chuyển chính hình vẽ, ta có thể di chuyển trục toạ độ. Rất đơn giản, chỉ cần cộng thêm 1 vector vào biến `st` lưu vị trí của mỗi fragment, sẽ khiến cho cả trục toạ độ di chuyển.
Nhìn trực tiếp thì sẽ dễ hơn giải thích, nên bạn hãy thử:
* Uncomment dòng 35 của đoạn code bên dưới
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Thử sửa code tiếp nhé:
* Kết hợp biến `u_time` với các hàm số đã biết để di chuyển hình chữ thập. Hãy tìm một chuyển động nào đó mà bạn thích, sau đó hãy mô phỏng chuyển động đó cho hình chữ thập này. Thực tế có rất nhiều chuyển động thú vị ở quanh bạn: những con sóng liên tiếp vỗ bờ này, chuyển động của con lắc này, quả bóng nảy trên mặt đất này, ô tô tăng tốc này rồi thì cái xe đạp dừng lại nữa.
Chuyển động quay
Để quay một vật thể ta cũng sẽ quay hệ trục toạ độ. Để làm việc đó, ta sẽ dùng tới ma trận. Ma trận gồm các số được xếp thành nhiều hàng và cột. Phép nhân một vector với một ma trận phải tuân thủ quy tắc một cách chính xác về thứ tự phép tính.
[](https://en.wikipedia.org/wiki/Matrix)
GLSL có sẵn một vài ma trận có kích thước khác nhau: `mat2` (2x2), `mat3` (3x3) và `mat4` (4x4). GLSL cũng có sẵn phép nhân ma trận thông thường (toán tử `*`) và cả phép nhân 2 ma trận theo từng cặp phần tử nữa (`matrixCompMult()`).
Ta có thể sử dụng ma trận để làm vài thứ hay ho. Ví dụ ta có thể dùng nó để tịnh tiến một vector:
Và hay hơn nữa, ta còn có thể dùng nó để xoay trục toạ độ:
Đoạn code dưới đây khởi tạo một ma trận 2 chiều. Hàm này áp dụng công thức ở hình trên để xoay một vector 2 chiều quanh gốc toạ độ `vec2(0.0)`.
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}Thực ra thì xoay 1 điểm quanh gốc toạ độ không phải là điều mà ta muốn, ta cần nó xoay quanh tâm của chính nó cơ. Và để làm điều đó, ta sẽ chữa mẹo như sau: đầu tiên di chuyển hình chữ thập về đúng gốc toạ độ để tâm của hình chữ thập trùng vị trí với gốc toạ độ, sau đó thực hiện phép quay, cuối cùng di chuyển hình chữ thập đã quay về lại vị trí cũ.
Code sẽ trông như sau:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Hãy thử:
* Uncomment dòng 45 xem chuyện gì xảy ra.
* Comment đoạn code thực hiện việc di chuyển hình chữ thập trước và sau khi thực hiện phép quay, ở dòng 37 và 39, xem chuyện gì xảy ra.
* Kết hợp phép quay với chuyển động tịnh tiến ở phần trước.
Phép thu phóng
Ta đã thấy cách mà ma trận được sử dụng để di chuyển và quay vật thể trong không gian. Nếu bạn đã từng sử dụng các phần mềm tạo mô hình 3D hay sử dụng tính năng push và pop ma trận trong Processing, thì bạn sẽ biết rằng ma trận còn có thể phóng to thu nhỏ vật thể nữa.
Từ công thức thu phóng 3 chiều trên đây, ta có thể suy luận ra cách tạo nên ma trận thu phóng 2 chiều:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}<div class="codeAndCanvas" data="cross-scale.frag"></div>
Hãy thử làm như sau để có thể hiểu sâu hơn cách ma trận thu phóng hoạt động:
* Uncomment dòng 42 để thấy rõ rằng cả hệ toạ độ được thu phóng chứ không phải chỉ mỗi hình chữ thập.
* Xem điều gì xảy ra nếu comment 2 dòng code di chuyển trước và sau khi thu phóng (37 và 39).
* Thử kết hợp ma trận quay với ma trận thu phóng. Chú ý rằng thứ tự thực hiện sẽ ảnh hưởng tới kết quả sau cùng. Hãy nhân các ma trận trước rồi nhân với vector sau cùng.
* Giờ thì không những bạn biết cách vẽ các hình rồi, mà còn biết cách di chuyển, quay và phóng to thu nhỏ chúng nữa. Hãy kết hợp nhiều hình lại để tạo nên một giao diện ảo. Bạn có thể tham khảo ví dụ sau.
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
Công dụng khác của ma trận: Không gian màu YUV
YUV là không gian màu sử dụng cách mã hoá analog truyền thống của ảnh về video, chỉ quan tâm tới dải màu mà con người có thể nhìn thấy được để thu gọn dữ liệu cần lưu trữ.
Đoạn code dưới đây sử dụng ma trận để chuyển đổi màu từ không gian này sang một không gian khác.
<div class="codeAndCanvas" data="yuv.frag"></div>
Bạn có thể thấy tôi thao tác với màu tương tự như với các vector khi đem chúng nhân với các ma trận.
Trong chương này ta đã học cách biến đổi ma trận để di chuyển, quay và thu phóng các vector. Các phép biến đổi này rất cần thiết để có thể kết hợp các hình mà ta đã học ở các chương trước. Ở chương tiếp theo, ta sẽ áp dụng tất cả những kiến thức này để tạo nên các hoa văn tuyệt đẹp. Bạn sẽ thấy việc đó ngầu thế nào cho mà xem.
---
08/SUMMARY Pt (08/SUMMARY-pt.md)
Matriz é uma ferramenta poderosa para manipular vetores. Ser experiente com matrizes lhe permite a, livremente, transladar, escalar e rotacionar formas. Uma vez que a técnica pode ser aplicada igualmente para qualquer coisa expressada por vetores, vamos dar uma olhada em usos muito mais avançados depois, neste livro. Matrizes podem árecer complexas à primeira vista, mas você vai achá-las bem úteis depois que pegar o conceito. Vamos praticar aqui e aprender o básico com alguns exemplos simples.
---
08/SUMMARY (08/SUMMARY.md)
Matrix is a very powerful tool for manipulating vectors. By mastering how to use matrices, you can freely translate, scale and rotate shapes. Since the technique can be equally applied to anything expressed by vectors, we will look at many more advanced use of matrices later in this book.
Matrices may look complex at a first glance, but you'll find it very handy and useful as you get used to the concept. Let's practice here and learn basics with simple examples.
---
09/README (09/README.md)
Patterns
Since shader programs are executed by pixel-by-pixel no matter how much you repeat a shape the number of calculations stays constant. This means that fragment shaders are particulary suitable for tile patterns.
In this chapter we are going to apply what we've learned so far and repeat it along a canvas. Like in previous chapters, our strategy will be based on multiplying the space coordinates (between 0.0 and 1.0), so that the shapes we draw between the values 0.0 and 1.0 will be repeated to make a grid.
"The grid provides a framework within which human intuition and invention can operate and that it can subvert. Within the chaos of nature patterns provide a constrast and promise of order. From early patterns on pottery to geometric mosaics in Roman baths, people have long used grids to enhance their lives with decoration." 10 PRINT, Mit Press, (2013)
First let's remember the `fract()` function. It returns the fractional part of a number, making `fract()` in essence the modulo of one (`mod(x,1.0)`). In other words, `fract()` returns the number after the floating point. Our normalized coordinate system variable (`st`) already goes from 0.0 to 1.0 so it doesn't make sense to do something like:
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}But if we scale the normalized coordinate system up - let's say by three - we will get three sequences of linear interpolations between 0-1: the first one between 0-1, the second one for the floating points between 1-2 and the third one for the floating points between 2-3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Now it's time to draw something in each subspace, by uncommenting line 27. (Because we are multiplying equally in x and y the aspect ratio of the space doesn't change and shapes will be as expected.)
Try some of the following exercises to get a deeper understanding:
* Multiply the space by different numbers. Try with floating point values and also with different values for x and y.
* Make a reusable function of this tiling trick.
* Divide the space into 3 rows and 3 columns. Find a way to know in which column and row the thread is and use that to change the shape that is displaying. Try to compose a tic-tac-toe match.
Apply matrices inside patterns
Since each subdivision or cell is a smaller version of the normalized coordinate system we have already been using, we can apply a matrix transformation to it in order to translate, rotate or scale the space inside.
<div class="codeAndCanvas" data="checks.frag"></div>
* Think of interesting ways of animating this pattern. Consider animating color, shapes and motion. Make three different animations.
* Recreate more complicated patterns by composing different shapes.
[](../edit.php#09/diamondtiles.frag)
* Combine different layers of patterns to compose your own Scottish Tartan Patterns.
Offset patterns
So let's say we want to imitate a brick wall. Looking at the wall, you can see a half brick offset on x in every other row. How we can do that?
As a first step we need to know if the row of our thread is an even or odd number, because we can use that to determine if we need to offset the x in that row. For that we are going to use `mod()` of `2.0` and then see if the result is under `1.0` or not. Take a look at the following formula and uncomment the two last lines.
<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
As you can see we can use a ternary operator to check if the `mod()` of `2.0` is under `1.0` (second line) or similarly we can use a `step()` function which does the same operation, but faster. Why? Although is hard to know how each graphic card optimizes and compiles the code, it is safe to assume that built-in functions are faster than non-built-in ones. Everytime you can use a built-in function, use it!
So now that we have our odd number formula we can apply an offset to the odd rows to give a brick effect to our tiles. Line 14 of the following code is where we are using the function to "detect" odd rows and give them a half-unit offset on `x`. Note that for even rows, the result of our function is `0.0`, and multiplying `0.0` by the offset of `0.5` gives an offset of `0.0`. But on odd rows we multiply the result of our function, `1.0`, by the offset of `0.5`, which moves the `x` axis of the coordinate system by `0.5`.
Now try uncommenting line 32 - this stretches the aspect ratio of the coordinate system to mimic the aspect of a "modern brick". By uncommenting line 40 you can see how the coordinate system looks mapped to red and green.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Try animating this by moving the offset according to time.
* Make another animation where even rows move to the left and odd rows move to the right.
* Can you repeat this effect but with columns?
* Try combining an offset on `x` and `y` axis to get something like this:
<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Truchet Tiles
Now that we've learned how to tell if our cell is in an even or odd row or column, it's possible to reuse a single design element depending on its position. Consider the case of the Truchet Tiles where a single design element can be presented in four different ways:
By changing the pattern across tiles, it's possible to construct an infinite set of complex designs.
Pay close attention to the function `rotateTilePattern()`, which subdivides the space into four cells and assigns an angle of rotation to each one.
<div class="codeAndCanvas" data="truchet.frag"></div>
* Comment, uncomment and duplicate lines 69 to 72 to compose new designs.
* Change the black and white triangle for another element like: half circles, rotated squares or lines.
* Code other patterns where the elements are rotated according to their position.
* Make a pattern that changes other properties according to the position of the elements.
* Think of something else that is not necessarily a pattern where you can apply the principles from this section. (Ex: I Ching hexagrams)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Making your own rules
Making procedural patterns is a mental exercise in finding minimal reusable elements. This practice is old; we as a species have been using grids and patterns to decorate textiles, floors and borders of objects for a long time: from meanders patterns in ancient Greece, to Chinese lattice design, the pleasure of repetition and variation catches our imagination. Take some time to look at decorative patterns and see how artists and designers have a long history of navigating the fine edge between the predictability of order and the surprise of variation and chaos. From Arabic geometrical patterns, to gorgeous African fabric designs, there is an entire universe of patterns to learn from.
With this chapter we end the section on Algorithmic Drawing. In the following chapters we will learn how to bring some entropy to our shaders and produce generative designs.
---
09/Notes (09/notes.md)
nicolas
* after line 23, i've added the following:
Once multiplied by 3, instead of ranging from 0 à 1 on the X and Y axes, `st` will range from 0 to 3 on X and Y axes.`
If we use st * 3.0` as such, its value will become greater than 1 and we won't be able to use it like we did so far ; it will not be normalised between 0 and 1 anymore.`
If we only use the fractional part, we will fall back to a normalised space as the values returned by fract()` is alwys comprised between 0 and 1.`
Here's an example of the respective values of st`, `st 3` and `fract( st 3 )`.`first block
st = 0.00 | st 3 = 0.00 | fract( st 3 ) = 0.00
st = 0.10 | st 3 = 0.30 | fract( st 3 ) = 0.30
st = 0.20 | st 3 = 0.60 | fract( st 3 ) = 0.60
st = 0.30 | st 3 = 0.90 | fract( st 3 ) = 0.90
second block
st = 0.40 | st 3 = 1.20 | fract( st 3 ) = 0.20
st = 0.50 | st 3 = 1.50 | fract( st 3 ) = 0.50
st = 0.60 | st 3 = 1.80 | fract( st 3 ) = 0.80
third block
st = 0.70 | st 3 = 2.10 | fract( st 3 ) = 0.10
st = 0.80 | st 3 = 2.40 | fract( st 3 ) = 0.40
st = 0.90 | st 3 = 2.70 | fract( st 3 ) = 0.70
st = 1.00 | st 3 = 3.00 | fract( st 3 ) = 1.00
We can clearly see that on the first block, the value of st 3` is the same as the value of `fract( st 3 )`but after the second block, is greater than 1 and `fract( st 3 )` remains comprised between 0 and 1.
* after line 70 I've added
In fact, a ternary operator is an `if` in disguise, the program is forced to branch and to evaluate both branches of the `if / else` which slows the execution down.`
In this case, we could write:y = floor( mod( x, 2.0 ) );
which will also return 0 if the modulo's result is lower than 1 and 1 if the modulo's result is comprised between 1 & 2.
The main difference being that it needs to load only one value in memory instead of 2 for a step()` evaluation.`
We could even get rid of the floor` by `casting` (transtyping) the modulo's result to `int` like so:`y = float( int( mod( x, 2.0 ) ) );
When casting a float` to `int`, we drop the number after the floating point which is the same as calling the `floor` function, (beware to re-cast the result to `float`).
---
09/README Ch (09/README-ch.md)
Patterns 图案
因为着色器按一个个像素执行,那么无论你重复一个图形多少次,计算的数量仍然是个常数。
本章中我们将综合我们目前所学的并应用在画布上。和前几章一样,我们的策略依然基于乘以空间坐标(0到1之间),这样我们的画的在0到1之间的图形就会重复地形成网格。
"网格提供一种基于人的直觉发明事物的框架,并且可以颠覆。自然的混沌肌理提供一种对比和秩序的迹象。从早期罗马浴场里的陶瓷图案到几何镶嵌,人们那时候就习惯用网格来点缀他们的生活。"10 PRINT, Mit Press, (2013)
首先让我们记住 `fract()` 函数。它返回一个数的分数部分,本质上是除1的余数(`mod(x,1.0)`)。换句话说, `fract()` 返回小数点后的数。 我们单位化的坐标系变量 (`st`) 已经是 0.0 到 1.0 之间的了。所以像下面这么做并没有必要:
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}但如果我们放大单位化坐标系 — 比如说3倍 — 我们会得到三组 0 到 1 的线性插值的数列:第一组在 0-1 之间,第二组浮点数在 1-2 之间以及第三组在 2-3 之间的浮点数。
<div class="codeAndCanvas" data="grid-making.frag"></div>
现在是时候在子空间(网格单元的空间)里画点什么了。取消27行的注释。(因为我们等比例放大了x和y坐标,所以不会改变坐标的比例,图形会和预期的一样。)
试试下面的练习来深入理解:
* 把空间乘以不同的数。试试用浮点数,还有分别给x和y不同的系数。
* 把这个平铺技巧做成一个可以反复使用的函数。
* 把画布分成 3 行 3 列。 指出如何定义行和列的线程的,并用这种方式改变显示着的图形。试着做一个井字棋。
在图案内部应用矩阵
鉴于每个细分或者说单元都是我们正在使用的单位化坐标系的小单元,我们可以对每个内部空间施以矩阵变换来平移,旋转和缩放。
<div class="codeAndCanvas" data="checks.frag"></div>
* 想想怎么让这些图案有趣的动起来。考虑颜色,形状,运动的变换。做三种动画。
* 通过组合不同的形状重新创造更复杂的图案。
<a href="../edit.php#09/diamondtiles.frag"><canvas id="custom" class="canvas" data-fragment-url="diamondtiles.frag" width="520px" height="200px"></canvas></a>
* 结合多层图案来制作你自己的 Scottish Tartan Patterns.
偏移图案
So let's say we want to imitate a brick wall. Looking at the wall, you can see a half brick offset on x in every other row. How we can do that?
假如我们想要模仿砖墙。看,下面的墙,你是不是看到一半的砖在x方向上偏移了一半砖的长度,没隔一行偏移一次。我们如何实现?
第一步我们需要知道某行的线程是奇数还是偶数,以为我们可以通过奇偶来决定是否要在x方向上偏移那一行。
____我们需要两段来解决这个问题____
要判断我们的线程是一个奇数行或者偶数行,我们要用 `2.0` 的 `mod()` 。 然后根据结果是否大于 `1.0` 来判断。看一下下面的函数,取消最后两行的注释。
<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
正如你所见,我们可以用一个 三元算符号 (第二行)来检查 `2.0` 的`mod()`(余数)小于 `1.0` 或者类似地,我们用 `step()` 函数做相同的操作,但(其实)更快。为什么呢? 因为虽然要知道每个显卡如何优化和编译代码并不容易,但是可以安全地假设内置函数总比非内置的函数快。任何时候你都以调用内置函数,干嘛不用呢!
现在我们有这些找出奇数的方程,这样我们就可以给奇数行一个偏移量,然后就可以把 砖块 做出拼砖的效果。下面代码的第14行便是我们用来“侦测”奇数行,并予之半个单位在x上的偏移的。注意到对偶数行,函数的返回值是 `0.0`, `0.0` 乘以 `0.5` 得到一个 `0.0` 的偏移。 但是奇数行我们用函数的返回值, `1.0`, 乘以偏移量 `0.5`,这样便向坐标系的 `x` 轴偏移了 `0.5`。
现在试着取消32行的注释 — 拉伸长宽比来模仿“现代砖块”的长宽比。通过取消第40行的的代码,你可以注意到坐标系统是如何看起来映射到红绿色的。
<div class="codeAndCanvas" data="bricks.frag"></div>
* 试着根据时间变化对偏移量做动画。
* 另做一个动画,让偶数行向左移,奇数行向右移动。
* 能不能根据列重复这样的效果?
* 试着结合 `x` 和 `y` 轴的偏移来得到下面这样的效果:
<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Truchet 瓷砖
目前我们学了如何区分奇数行/列或偶数行/列,(类似的),(我们也)可能再用(这个技巧)根据位置来设计元素。 考虑到 Truchet Tiles 的例子,即一个单一设计元素可以以四种不同的方式呈现:
通过改变对角瓷砖的图案,便可能组成无限种复杂设计的可能。
仔细观察 `rotateTilePattern()` 函数, 它把坐标空间细分成四个单元并赋予每一个旋转值。
<div class="codeAndCanvas" data="truchet.frag"></div>
* 注释,取消注释,以及复制第69到72行来创作新的设计。
* 把黑白三角变成其他元素,例如:半圆,旋转的方形或直线。
* 编写根据元素自身位置旋转的图形代码。
* 创作一个根据其位置改变其他属性的图案。
* 想想其他能用这章原理的案例,不一定是图案. (例: 易经卦)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
制定自己的规则
制作程序图案是种寻找最小可重复元素的古老练习(灵修)。我们作为长时间使用网格和图案来装饰织物、地面和物品的镶边物种:从古希腊的弯曲图案,到中国的窗栅设计,重复和变化的愉悦吸引我们的想象。花些时间浏览 decorative patterns 并看看在漫长的历史里,艺术家和设计师是如何寻找在秩序的预测性和(由)混沌和衍变(产生)的惊奇之间的边界的。从阿拉伯几何图案,到斑斓的非洲编制艺术,这里有一整个图案的宇宙要学习。
这章我们就结束算法绘图部分了。在接下来的章节,我们会学习如何把熵(其实就是混乱和随机的意思)加入到我们的着色器中来产生生成设计。
---
09/README De (09/README-de.md)
Muster
Weil Shader-Programme Bildpunkt für Bildpunkt ausgeführt werden, spielt es keine große Rolle, wie oft man ein bestimmtes Muster innerhalb der Zeichenfläche wiederholt – die Anzahl der Berechnungen bleibt gleich. Aus diesem Grund eignen sich Fragment-Shader ganz hervorragend für die Erzeugung sich wiederholender Kachelmuster.
In diesem Kapitel werden wir anwenden, was wir bislang gelernt haben, und es über den gesamten Raum unserer Zeichenfläche wiederholen. Wie schon in den vorhergehenden Kapiteln wird unsere Strategie darin bestehen, die Koordinaten zu vervielfältigen. Dadurch werden sich Formen, die wir innerhalb des Koordinatenraumes zwischen `0.0` und `1.0` zeichnen, vervielfältigen lassen, so dass ein kontinuierliches Muster entlang eines Rasters entsteht.
"Das Gitternetz eröffnet einen Rahmen, innerhalb dessen sich die menschliche Intuition und Schöpfungskraft entfalten kann. Inmitten des Chaos der Natur stellen Muster einen Kontrastpunkt dar, indem sie eine vorhersehbare, sich wiederholende Ordnung realisieren. Schon seit frühen Mustern auf Keramikflächen in antiken Bädern haben die Menschen Gitternetze genutzt, um ihr Leben durch repetitive Dekorationen zu verschönen.“ 10 PRINT, Mit Press, (2013)
Lasst uns zunächst noch einmal die `fract()`-Funktion ins Gedächtnis rufen. Sie liefert den Nachkommateil einer Zahl zurück, wodurch `fract()` effektiv den Modulo (Rest) einer Division durch 1 ergibt (`mod(x,1.0)`). Unsere normalisierte Variable für das Koordinatensystem (`st`) bewegt sich ohnehin zwischen `0.0` und `1.0`. Deshalb macht es wenig Sinn, Berechnungen wie die folgende anzustellen:
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}Wenn wir aber das Koordinatensystem hochskalieren – sagen wir einmal um den Faktor drei – erhalten wir drei interpolierende Sequenzen: die Erste mit Fließkommawerten von `0` bis `1`, die Zweite mit Werten von `1` bis `2` und die Dritte mit Werten zwischen `2` und `3`.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Jetzt ist es an der Zeit, in jedem dieser Drei mal Drei „Unterräume“ zu zeichnen, indem Du die Kommentarzeichen in Programmzeile 27 entfernst. (Weil wir die x- und y-Ordinate gleichermaßen skalieren, bleibt das Seitenverhältnis erhalten.)
Probiere einige der folgenden Übungen aus, um Dein Verständnis zu vertiefen:
Multipliziere die Koordinaten (den Raum) mit anderen Faktoren. Probiere auch einmal nicht ganzzahlige Werte, sowie unterschiedliche Faktoren für x und y* aus.
* Verwandle diesen „Muster-Trick“ in eine wiederverwendbare Funktion.
* Teile den Raum in drei Spalten und drei Zeilen auf. Finde einen Weg, wie Dein Programmcode erkennen kann, an welcher Zeile und Spalte er gerade arbeitet und nutze dies, um die gezeichneten Formen zu variieren. Versuche, ein Tick-Tack-Toe-Spielfeld nachzuempfinden.
Anwendung von Matrizen innerhalb von Mustern
Weil jeder „Unterraum“ und jede Gitterzelle eine verkleinerte Fassung des normalisierten Koordinatensystems darstellt, mit dem wir bereits gearbeitet haben, können wir auch Matrizenoperationen darauf anwenden. So lässt sich der Koordinatenraum innerhalb einer Gitterzelle verschieben, rotieren und auch skalieren.
<div class="codeAndCanvas" data="checks.frag"></div>
* Überlege Dir interessante Wege, um dieses Muster zu animieren. Denke z.B. an die Animation von Farbe, Form oder Bewegung. Erstelle drei unterschiedliche Animationen.
* Erzeuge komplexere Muster durch das Zusammenfügen unterschiedlicher Formen.
[](../edit.php#09/diamondtiles.frag)
* Vereine unterschiedliche Schichten von Mustern, um Dein eigenes Karomuster entstehen zu lassen, wie es für schottische Tartans typisch ist).
Versetzte Muster
Sagen wir einmal, wir wollen ein Mauerwerk zeichnen. Beim Blick auf eine solche Wand fällt sofort auf, dass die Steine in jeder Reihe jeweils um eine halbe Steinlänge versetzt sind. Stellt sich natürlich die Frage, wie wir so etwas per Programmcode umsetzen können?
Im ersten Schritt müssen wir zunächst einmal wissen, ob die Zeile, an der unser Thread gerade arbeitet, gerade oder ungerade ist. Denn darüber können wir den Versatz der Muster entlang der x-Achse bestimmen. Dafür können wir `mod()` mit `2.0` verwenden und schauen, ob das Ergebnis weniger als `1.0` beträgt oder nicht. Wirf einen Blick auf die folgende Formel und entferne nacheinander die Kommentarzeichen in den letzten beiden Zeilen.
<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
Wie Du siehst, können wir den ternären Operator nutzen, um zu prüfen, ob das Ergebnis von `mod()` mit `2.0` unter dem Wert von `1.0` liegt. Oder wir können die `step()`-Funktion nutzen, um das gleiche Ergebnis zu erzielen. Und `step()` ist in der konkreten Ausführung sogar noch etwas schneller. Doch wie kommt das? Obwohl es schwer vorhersehbar ist, wie eine bestimmte Grafikkarte Shader-Code kompiliert und optimiert, können wir davon ausgehen, dass eingebaute Funktionen schneller ausgeführt werden, also solche, die erst in mehrere Einzelschritte aufgelöst werden müssen. Wann immer Du eine eingebaute Funktion für Deine Zwecke nutzen kannst, tue dies deshalb auch!
Nun haben wir also unsere „Ungerade-Funktion“ beisammen, sodass wir den ungeraden Reihen unseres Mauerwerks einen Versatz geben können. In der Programmzeile 14 des folgenden Listings befindet sich der entsprechende Programmcode. Beachte, dass das Ergebnis unserer Berechnung für gerade Reihen `0.0` lautet und die Multiplikation mit dem Versatz von `0.5` dann eben auch `0.0` ergibt. Bei ungeraden Reihen multiplizieren wir den Versatz von `0.5` hingegen mit `1.0`, so dass daraus eine entsprechende Verschiebung der x-Achse des Koordinatensystems folgt.
Entferne jetzt die Kommentarzeichen in der Programmzeile 32, wodurch das Seitenverhältnis unseres Koordinatensystems gestreckt wird und wir die Anmutung eines modernen Mauerwerks erhalten. Wenn Du die Kommentarzeichen in Programmzeile 40 entfernst, erkennst Du die Anordnung und Wiederholung des Koordinatensystems anhand der Farben Rot und Grün.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Versuche diese Grafik zu animieren, indem Du den Versatz auf Basis der Zeit veränderst.
* Baue eine weitere Animation, bei der sich die geraden Reihen nach links und die ungeraden Reihen nach rechts bewegen.
* Gelingt es Dir, diesen Effekt auch in Bezug auf die Spalten zu wiederholen?
Versuche einen Versatz entlang der x-Achse mit einem entlang der y-Achse* zu kombinieren, um so etwas wie die folgende Animation zu erhalten:
<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Truchet-Kacheln
Wir haben nun gelernt, wie man feststellt, ob sich die zu zeichnende Gitterzelle in einer geraden oder ungeraden Zeile bzw. Spalte befindet, Auf Basis dieser Information können wir ein einzelnes Designelement in Abhängigkeit seiner Position wiederverwenden. Wirf einen Blick auf die sogenannten Truchet-Kacheln, bei denen eine einzelne Form in vier unterschiedlichen Anordnungen dargestellt wird:
Indem wir die Anordnung des Musters von Kachel zu Kachel variieren, können wir eine unendliche Menge von Designs erzeugen.
Schaue Die die Funktion `rotateTilePattern()` genau an. Sie unterteilt den Raum jeweils in vier Zellen und dreht jede davon in eine unterschiedliche Richtung.
<div class="codeAndCanvas" data="truchet.frag"></div>
Kommentiere die Programmzeilen 69 bis 72*, entferne die Kommentare wieder und vervielfältige diese Zeilen, um neue Designs entstehen zu lassen.
* Ersetze das Schwarzweiß-Dreieck durch andere Formen, beispielsweise Halbkreise, gedrehte Quadrate oder Linien.
* Kodiere andere Muster, bei denen die Elemente in Abhängigkeit ihrer Position unterschiedlich gedreht werden.
* Entwickle ein Muster, das weitere Merkmale in Abhängigkeit der Elementposition verändert.
* Denke an andere Darstellungen, die nicht notwendigerweise Muster verkörpern müssen, aber die Konzepte aus diesem Abschnitt aufgreifen, beispielsweise an das chinesische I-Ging.
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Erschaffe Deine eigenen Regeln
Prozedurale, sich wiederholende Muster zu kreieren, ist eine interessante Übung für den Geist. Es geht darum, den minimalen Satz an Elementen zu finden, die sich wiederholen müssen, um ein größeres komplexes Ganzes zu erschaffen. Diese Vorgehensweise ist sehr alt. Die Menschheit benutzt Muster und Gitternetze schon seit tausenden von Jahren, um Wände und Böden, Textilien und andere Objekte zu verzieren. Angefangen von mäandernden Mustern im alten Griechenland bis hin zu chinesischen Gitterfenstern, ist es immer wieder das Spiel aus Wiederholung bei gleichzeitiger Variation, die unsere Fantasie anregt. Nimm Dir ein wenig Zeit, um diese Art von Dekorationen und Mustern zu studieren. Von geometrischen Mustern aus Arabien bis hin zu prachtvollen Stoffmustern aus dem afrikanischem Kulturkreis eröffnet sich Dir ein ganzes Universum an Mustern, von denen Du lernen kannst.
Mit diesem Kapitel enden die Abschnitte zum algorithmischen Zeichnen. In den folgenden Kapiteln werden wir sehen, wie man ein wenig mehr Zufall in unsere Shader bringt, um die Natur nachzuempfinden.
---
09/README Fa (09/README-fa.md)
الگو ها
از آنجا که شیدر ها پیکسل به پیکسل اجرا میشوند، مهم نیست که چقد یک شکل را تکرار کنید. محاسبات ثابت است. این به معنی آن است که شیدر ها برای الگو ها و پترن های تکرار شونده مناسب هستند.
در این فصل قصد داریم آنچه تا کنون آموخته ایم را بکار ببریم و در امتداد کنوس اشکال را تکرار کنیم. مانند فصل های قبلی استراتژی ما مبتی بر عملیات رو مختصات فضای بین 0.0 و 1.0 است، به این ترتیب شکل هایی که بین 0.0 و 1.0 کشیده بودیم تکرار میشوند.
"شبکه بندی چارچوبی را فراهم میکند که به انسان ها قدرت شهود و اختراع میدهد. در هرج و مرج های الگو های طبیعی هم یک انعطاف و نظم نهفته است، از الگو های اولیه روی سفال گرفته تا موزاییک های هندسی در حمام های رومی، مدت هاست مردم از شبکه ها برای بهبود و تزئیین زندگی خود استفاده میکنند " 10 PRINT, Mit Press, (2013)
بیایید اول تابع `fract()` را بیاد بیاوریم. قسمت اعشاری یک عدد را بر میگرداند, `fract()` مانند (`mod(x,1.0)`) است. به عبارت دیگر, `fract()` عدد بعد از نقطه اعشار را بر میگرداند. سیستم محتصات نرمالایز شده ما (`st`) بین 0.0 و 1.0 است، پس منطقی نیست همچین کاری انجام دهم:
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}اما اگر سیستم مختصات را به بالا اسکیل کنیم - مثلا سه برابر - ما سه توالی درون خطی بین 0و1 داریم: اولی بین 0و1 است, بعد از آن مربوط به نقاط شناور بین 1 و 2 و همچنین بعد از آن نقاط شناور بین 2 و 3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
حال وقت آن است با برداشتن خط 27 در هر زیر فضا، شکلی ترسیم کنیم. (از آنجا که x,y ما به یک اندازه ضرب میشوند، نسبت ابعاد فضا تغییری نمیکند و اشکال مطابق انتظار تغییر میکنند.)
برخی از تمرینات زیر را امتحان کنید تا درک عمیق تری پیدا کنید:
* فضا را در اعداد مختلف ضرب کنید، با مقادیر اعشاری و همچنین مقادیر مختلف برای x و y.
* از عملکرد فوق، تابعی که قابل استفاده مجدد باشد بسازید.
* فضا را به سه ردیف و سه ستون تقسیم کنید، راهی پیدا کنید که بفهمید هر ترد در کدام ستون و ردیف است، از آن برای تغییر شکل و نشان دادن آن استفاده کنید. سعی کنید شکل یک بازی دوز را بسازید.
اعمال ماتریس داخل الگو ها
از آنجا که هر سلول نسخه کوچکتری ار سیستم مختصات نرمال شده است، میتوان مثل قبل برای چرخش و انتقال از ماتریس ها استفاده کرد.
<div class="codeAndCanvas" data="checks.frag"></div>
* به روش های جالب تری برای متحرک سازی این الگو ها فکر کنید، رنگ و شکل و و حرکت را میتوانید انیمیت کنید، سه انیمیشن متفاوت بسازید.
* با ترکیب اشکال مختلف الگو های پیچیده تر را از نو بسازید.
[](../edit.php#09/diamondtiles.frag)
* لایه های مختلف پترن را با هم ترکیب کنید و سعی کنید الگوی اسکاتلندی خود را بسازید.
جابجایی الگو ها
فرض کنیم میخواهیم یک دیوار آجری بسازیم. با نگاه کردن به دیوار میفهمید هر ردیف، نصف اندازه آجر جابه جا شده است چگونه میتوانیم این کار را انجام دهیم?
برای اولین قدم باید بفهمیم ردیف ترد ما یک عدد زوج است یا فرد. چون میتوانیم با دانستن آن، مقدار x را در آن ردیف جابجا کنیم.
____دو پاراگراف بعدی را با کمک هم باید اصلاح کنیم____
برای تعیین این که نخ ما در ردیف زوج یا فرد است از تابع باقیمانده `mod()` بر عدد 2 استفاده میکنیم و سپس مشاهده کنیم که آیا نتیجه زیر 1.0 است یا خیر. به فرمول زیر نگاهی بیاندازید و دو خط آخر را از حالت کامنت خارج کنِد.
<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
همانطور که در خط دوم مثال بالا دیدید، میتوانیم از اپراتور سه گانه استفاده کنیم و چک کنیم اگر `mod()` بر 2 زیر 1 است یا بطور مشابه در خط سوم از تابع `step()` استفاده کنیم, روش دوم سریع تر است، اما چرا؟ میدانیم دانستن عملیات اجرا و بهینه سازی هر نوع کارت گرافیک دشوار است، اما توابع داخلی سریعتر اجرا میشوند، پس هر موقع میتوانستید از توابع داخلی استفاده کنید، حتما بجای کد های شرطی از آنان استفاده کنید.
بنابراین اکنون که فرمول اعداد فرد خود را داریم، میتوان برای ردیف های فرد عملیات جابه جایی را انجام دهیم، خط 14 در مثال زیر جایی است که ردیف های فرد را شناسایی میکنیم و آنان را نیم واحد جابجا میکنیم. توجه داشته باشید که برای ردیف های زوج نتیجه عملکرد ما 0.0 است و ضرب 0.0 در مقدار جابه جایی 0.5 هیچ مقداری را جابجا نمیکند. اما در ردیف های فرد با ضرب 1.0 در مقدار جابجایی 0.5، آن ردیف را در محور مختصات 0.5 واحد جابجا میکنیم.
حال سعی کنید خط 32 را از کامنت خارج کنید، این کار نسبت ابعاد مختصات را میکشد تا شکل آجر مانند را تقلید کند. با خارج کردن حالت کامنت خط 40 میتوانید ببینید چگونه سیستم مختصات به رنگ قرمز و سبز ترسیم شده است.
<div class="codeAndCanvas" data="bricks.frag"></div>
* سعی کنید جابجایی را بر حسب زمان متحرک سازی کنید.
* انیمیشن های دیگری بسازید که ردیف های زوج به سمت چپ و ردیف های فرد به سمت راست حرکت کنند.
* آیا این کار را برای ستون ها میتوانید انجام دهید؟
* سعی کنید جابجایی در x و y را ترکیب کنید و چیزی مثل شکل زیر بسازید:
<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
کاشی های Truchet
حال که میتوانیم بفهمیم سلول هایمان در ستون ها و ردیف های زوج یا فرد هستند, پس بسته به موقعیت سلول ها میتوان یک طراحی واحد را چندین بار استفاده کرد. الگوی Truchet Tiles را در نظر بگیرید، که در آن از یک عنصر طراحی واحد میتوان به چهار روش زیر استفاده کرد:
با تغییر الگو در میان کاشی ها، میتوان مجموعه ای بینهایت از طرح های مختلف و پیچیده ساخت.
به تابع `rotateTilePattern()` خوب دقت کنید, که چگونه فضا را به چهار سلول تقسیم میکند و به هر کدام زاویه چرخشی اختصاص میدهد.
<div class="codeAndCanvas" data="truchet.frag"></div>
* خط های 69 تا 72 را از حالت کامنت خارج کنید، تا طرح های جدید را ببینید.
* الگوی مثلث سیاه و سفید را با الگویی دیگری مثل نیم دایره، مربع یا خطوط تغییر دهید.
* الگو های دیگری که عناصر آن با توجه به موقعیتشان میچرخند بسازید.
* الگویی بسازید که سایز خواص را با توجه به موقعیت سلول ها تغییر دهد.
* به چیز دیگری فکر کنید. چیزی که لزوما الگویی و پترنی نباشد تا بتوانید اصول این بخش را کامل یاد بگیرید چیزی مثل: (I Ching hexagrams)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
قوانین خودتان را بسازید
ایجاد الگو های رویه ای یک نوع تمرین ذهنی برای یافتن حداقل عناصر قابل استفاده مجدد است، این یک عمل قدیمی است; انسان ها برای مدت طولانی است که از شبکه ها و الگو ها برای تزئین پارچه، کف زمین و اطراف اشیا استفاده میکنند، از الگو های داری پیچ و خم در یونان باستان، تا طراحی مشبک چینی، لذت تکرار و تغییر عناصر، چشم ما را جلب میکند. نگاهی به decorative patterns بیاندازید و ببینید که چگونه هنرمندان و طراحان سابقه طولانی در پیمیاش مرز بین قابل پیش بینی بودن و تنوع و هرج و مرج دارند. از الگو های هندسی عربی گرفته تا طرح های پارچه ای و زیبای آفریقایی، الگو های زیادی برای یادگیری وجود دارد.
با اتمام این فصل درس های ما در مورد الگوریتم های شکل دهی پایان یافت، در فصل های بعد می آموزیم چگونه برخی آنتروپی ها را به شیدر خورد اضافه کنیم و طرح های مولد تولید کنیم.
---
09/README Fr (09/README-fr.md)
Motifs
Les shaders sont exécutés au pixel et quel que soit le nombre de répétitions d'une forme, le nombre d'opérations effectuées restera constant.
Cela signifie que les fragments shaders sont tout à fait indiqués pour la création de motifs répétitifs ; des pavages du plan.
Dans ce chapitre, nous allons mettre en application tout ce que nous avons vu jusqu'à présent et le répéter sur le canvas.
Comme dans les chapitres précédents, notre stratégie consistera à multiplier les coordonnées pour que les formes que nous dessinons restent dans un espace normalisé entre 0 et 1.
Le fait de multiplier les coordonnées du fragment en X et en Y créera une grille et le fait de diviser les coordonnées étirera une portion du canvas.
*"The grid provides a framework within which human intuition and invention can operate and that it can subvert.
Within the chaos of nature patterns provide a constrast and promise of order.
From early patterns on pottery to geometric mosaics in Roman baths, people have long used grids to enhance their lives with decoration." 10 PRINT*, Mit Press, (2013)
Pour commencer, rappelons nous de la méthode `fract()`.
Elle retourne la partie fractionnelle d'un chiffre, ce qui revient à considérer `fract()` comme le modulo de 1 (`mod(x,1.0)`).`
Autrement dit, fract()` retourne les chiffres après la virgule.`
Notre variable normalisée (st`) va déjà de 0.0 à 1.0 donc il n'est pas très judicieux d'écrire quelque chose comme :
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}En revanche, si nous multiplions la valeur de `
st` par 3, l'utilisation de `fract()` nous permettra d'obtenir trois séries de valeurs normalisées entre 0 et 1 :
la première série correspondra au moment où `st` se trouve entre 0 et 1, la seconde série correspondra au moment où `st` se trouve entre 1 et 2 et la troisième série correspondra au moment où `st` se trouve entre 2 et 3.Une fois multiplié par 3, et au lieu de passer de 0 à 1 en X et Y,
`st` passera de 0 à 3 en X et Y.
Si nous utilisons `st * 3.0` tel quel, sa valeur va vite dépasser 1 et nous ne pourrons plus nous en servir comme nous l'avons fait jusqu'à présent ; il ne sera plus normalisé entre 0 et 1.
Si on utilise seulement la partie fractionnelle, on retombe toujours dans un espace normalisé puisque la valeur de `fract()` (les chiffres après le virgule) sera toujours comprise entre 0 et 1.
Voici un exemple des valeurs respectives de `st`, `st 3` et `fract( st 3 )`.glsl// Premier bloc
st = 0.00 | st 3 = 0.00 | fract( st 3 ) = 0.00
st = 0.10 | st 3 = 0.30 | fract( st 3 ) = 0.30
st = 0.20 | st 3 = 0.60 | fract( st 3 ) = 0.60
st = 0.30 | st 3 = 0.90 | fract( st 3 ) = 0.90
// Second bloc
st = 0.40 | st 3 = 1.20 | fract( st 3 ) = 0.20
st = 0.50 | st 3 = 1.50 | fract( st 3 ) = 0.50
st = 0.60 | st 3 = 1.80 | fract( st 3 ) = 0.80
// Troisième bloc
st = 0.70 | st 3 = 2.10 | fract( st 3 ) = 0.10
st = 0.80 | st 3 = 2.40 | fract( st 3 ) = 0.40
st = 0.90 | st 3 = 2.70 | fract( st 3 ) = 0.70
st = 1.00 | st 3 = 3.00 | fract( st 3 ) = 1.00
On voit bien que sur le premier bloc, la valeur
`st 3` est égale à `fract( st 3 )` mais dès le second bloc, `st 3` dépassse 1 alors que `fract( st 3 )` reste toujours compris entre 0 et 1.<div class="codeAndCanvas" data="grid-making.frag"></div>
Essayons de dessiner quelque chose dans chaque sous-espace en décommentant la ligne 27.
Comme nous ne changeons pas le ratio (le rapport largeur/hauteur) en x et y, la forme ne change pas après la multiplication de taille et notre forme s'affiche correctement ; notre cercle est bien circulaire.
Essayez les choses suivantes pour mieux comprendre :
* Changez le facteur de multiplication des coordonnées
`st`, essayez des valeurs inférieures à zéro et des valeurs différentes en X et Y.* Créez un fonction réutilisable pour cette manipulation.
* Divisez l'espace en 3 lignes et trois colonnes.
Trouvez un moyen de connaître la case dans laquelle se trouve le fragment en cours de traitement et changez la forme dessinée dans la case en fonction de ça.
Essayez de faire un morpion.
Appliquer une matrice à l'intérieur d'une cellule
Sachant que chaque subdivision est une reproduction du motif original, nous pouvons appliquer une matrice de transformation pour déplacer, pivoter ou redimensionner l'espace dans chaque cellule.
<div class="codeAndCanvas" data="checks.frag"></div>
* Pensez à quelques façons intéressantes d'animer les couleurs et les formes de ce motif. Faites-en trois versions.
* Créez des motifs plus complexes en combinant plusieurs formes.
[](../edit.php#09/diamondtiles.frag)
* Combinez différentes couches de motifs pour composer votre propre Tartan écossais.
Décaler les motifs
Imaginons que nous voulions reproduire un motif mur de briques. En regardant le mur, nous constatons que chaque rangées est décalée de moitié par rapport à la précédente.
Comment pourrions-nous faire ça ?
En premier lieu, nous devons savoir si la rangée sur laquelle se trouve notre fragment est paire ou impaire.
Nous pourrons ensuite utiliser cette information pour savoir si on doit ou non se décaler sur l'axe X.
Pour déterminer si notre fragment est sur une ligne paire ou impaire, nous allons nous servir du résultat du modulo
`mod()` de `2.0`.
Si le modulo 2 est inférieur à 1, nous sommes sur une ligne paire, s'il est supérieur ou égal à 1, nous sommes sur une ligne impaire.Pour illustrer l'effet du modulo, regardez la formule suivante et décommentez les deux dernières lignes.
<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
On peut utiliser un opérateur ternaire) pour vérifier si le modulo (
`mod()`) renvoie une valeur inférieure à 1 (ligne 2)
ou bien on peut se servir d'un `step()` qui renvoie le même résultat plus vite.Pourquoi ? Bien qu'il soit difficile de dire comment chaque carte graphique compile et optimise le code, on peut partir du principe que les fonctions natives sont plus rapides que les fonctions personnalisées.
Chaque fois que vous pouvez utiliser une fonction native, faites le !
En fait un opérateur ternaire est un
`if` déguisé, le programme est obligé de créer et d'évaluer les deux branches du `if` ce qui ralentit son exécution. En l'occurrence, on pourrait écrire :glsly = floor( mod( x, 2.0 ) );
qui renverra également 0 si le modulo est inférieur à 1 et 1 si le modulo est compris entre 1 et 2. la différence étant qu'il n'y a besoin de charger qu'une seule valeur en mémoire contre 2 pour un step().
on pourrait même se dispenser du `floor` en utilisant un `cast` (transtypage) en `int` (nombre entier):
glsly = float( int( mod( x, 2.0 ) ) );
Lorsqu'on transtype un
`float` en `int` les chiffres après la virgule sont ignorés ce qui revient à appeler la méthode `floor` (en revanche, on doit re-caster le résultat en `float`).Maintenant que nous avons un moyen de déterminer si on a un chiffre pair ou impair, on peut s'en servir pour décaler les rangées impaires et obtenir notre mur de briques.
Ligne 14, nous avons une fonction qui détecte les rangées impaires et leur ajoute un décalage de 0.5 en
`x`.
Notez que pour les rangées paires, le résultat sera `0.0` et multiplier `0.0` par un décalage de `0.5` donnera un décalage de `0.0`.
Sur les rangées impaires, le résultat sera `1.0` qui, multiplié par `0.5`, décalera la position `st` de `0.5` sur l'axe des `x`.Essayez à présent de commenter la ligne 32, cela va changer le ratio (rapport entre largeur et hauteur) et étirer les coorodnnées pour produire une brique moderne.
En décommentant la ligne 40, on peut visualiser la distorsion grâce aux canaux rouge et vert.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Essayez d'animer le motif en faisant varier la quantité de décalage en fonction du temps.
* Créez une autre animation où les rangées paires bougent vers la droite et les rangées impaires bougeant vers la gauche.
* Pouvez vous appliquer cet effet sur les colonnes?
* Essayez de combiner les décalages en
`x` et `y` pour obtenir quelque chose comme ça :<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Pavage de Truchet
Maintenant que que nous avons vu comment déterminer si une cellule est sur une ligne ou une colonne paire ou impaire, il nous est possible de réutiliser un motif en fonction de sa position.
Prenons le pavage de Truchet + où un élément simple est présent sous quatre formes :
En changeant de motif d'une case à l'autre, on peut construire une infinité de pavages complexes.
Regardez attentivement la fonction
`rotateTilePattern()`, qui subdivise l'espace en quatre cellules et assigne une rotation à chacune.<div class="codeAndCanvas" data="truchet.frag"></div>
* Commentez / décommentez et dupliquez les lignes 69 à 72 pour composer de nouveaux motifs.
* Remplacez le triangle blanc par une autre forme : demi cercle, carré pivoté ou lignes.
Créez d'autres motifs suivant la règle pair/impair*.
Variez les propriétés affectées par l'ordre pair/impair* des cellules.
* Pensez à quelque chose d'autre qui ne soit pas nécessairement un pavage du plan et où vous pourriez appliquer les principes vus dans cette section (exemple : les Hexagrammes YI Jing)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Créer ses propres pavages
Créer des motifs procéduraux est un exercice cérébral consistant à trouver un ensemble minimal d'éléments réutilisables.
C'est une pratique très ancienne, en tant qu'espèce, nous avons utilisé les grilles et les motifs pour décorer les textiles, les sols et les objets depuis la nuit des temps.
Des frises grecques aux lattices chinoises, le plaisir des variations et des répétitions est une grande source d'inspiration.
Prenez le temps de vous intéresser aux motifs ornementaux
et constatez que de tout temps, artistes et designers ont navigué entre un ordre prédictible et les variations, accidentelles et chaotiques.
Des motifs islamiques aux textiles africains, nous disposons d'un univers de motifs à explorer.
Ce chapitre clos le dessin algorithmique. Dans les chapitres suivants, nous apprendrons à ramener un peu d'entropie et à produire des designs génératifs.
---
09/README Id (09/README-id.md)
Pola
Karena program shader dijalankan oleh piksel demi piksel tidak peduli seberapa banyak Anda mengulang bentuk, jumlah kalkulasi tetap konstan. Ini berarti bahwa shader fragmen sangat cocok untuk pola ubin.
Dalam bab ini kita akan menerapkan apa yang telah kita pelajari sejauh ini dan mengulanginya di sepanjang kanvas. Seperti pada bab-bab sebelumnya, strategi kita akan didasarkan pada perkalian koordinat ruang (antara 0,0 dan 1,0), sehingga bentuk yang kita gambar di antara nilai 0,0 dan 1,0 akan diulang untuk membuat kisi.
"Grid menyediakan kerangka kerja di mana intuisi dan penemuan manusia dapat beroperasi dan dapat menumbangkan. Dalam kekacauan pola alam memberikan batasan dan janji ketertiban. Dari pola awal pada tembikar hingga mosaik geometris di pemandian Romawi, orang sudah lama menggunakan kisi-kisi untuk meningkatkan kehidupan mereka dengan dekorasi. " 10 PRINT, Mit Press, (2013)
`fract()`. Ini mengembalikan bagian pecahan dari sebuah angka, membuat `fract()` pada dasarnya adalah modulo dari satu (`mod(x, 1.0)`). Dengan kata lain, `fract()` mengembalikan angka setelah titik mengambang. Variabel sistem koordinat kami yang dinormalisasi (`st`) sudah berubah dari 0,0 menjadi 1,0 sehingga tidak masuk akal untuk melakukan sesuatu seperti:glslvoid main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}
Tetapi jika kita meningkatkan skala sistem koordinat yang dinormalisasi - katakanlah tiga - kita akan mendapatkan tiga urutan interpolasi linier antara 0-1: yang pertama antara 0-1, yang kedua untuk floating point antara 1-2 dan yang ketiga satu untuk floating point antara 2-3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Sekarang saatnya menggambar sesuatu di setiap subruang, dengan menghapus komentar pada baris 27. (Karena kita mengalikan dengan sama dalam x dan y, rasio aspek ruang tidak berubah dan bentuk akan seperti yang diharapkan.)
Cobalah beberapa latihan berikut untuk mendapatkan pemahaman yang lebih dalam:
* Kalikan spasi dengan angka yang berbeda. Coba dengan nilai floating point dan juga dengan nilai x dan y yang berbeda.
* Buat fungsi yang dapat digunakan kembali dari trik ubin ini.
* Bagilah ruang menjadi 3 baris dan 3 kolom. Temukan cara untuk mengetahui di kolom dan baris mana utas tersebut dan gunakan itu untuk mengubah bentuk yang ditampilkan. Cobalah untuk membuat pertandingan tic-tac-toe.
Menerapkan matriks di dalam pola
Karena setiap subdivisi atau sel adalah versi yang lebih kecil dari sistem koordinat yang dinormalisasi yang telah kita gunakan, kita dapat menerapkan transformasi matriks padanya untuk menerjemahkan, memutar, atau menskalakan ruang di dalamnya.
<div class="codeAndCanvas" data="checks.frag"></div>
* Pikirkan cara-cara menarik untuk menghidupkan pola ini. Pertimbangkan untuk membuat animasi warna, bentuk, dan gerakan. Buat tiga animasi berbeda.
* Buat kembali pola yang lebih rumit dengan menyusun berbagai bentuk.
[](../edit.php#09/diamondtiles.frag)
* Gabungkan berbagai lapisan pola untuk membuat [Pola Tartan Skotlandia] Anda sendiri Scottish Tartan Patterns.
Pola offset
Jadi katakanlah kita ingin meniru dinding bata. Melihat ke dinding, Anda dapat melihat setengah bata pada x di setiap baris lainnya. Bagaimana kita bisa melakukannya?
Sebagai langkah pertama kita perlu mengetahui apakah baris utas kita adalah bilangan genap atau ganjil, karena kita dapat menggunakannya untuk menentukan apakah kita perlu mengimbangi x di baris itu.
____kita harus memperbaiki dua paragraf berikutnya ini bersama-sama____
Untuk menentukan apakah utas kita berada di baris ganjil atau genap, kita akan menggunakan
`mod()` dari `2.0` dan kemudian lihat apakah hasilnya di bawah `1.0` atau tidak. Perhatikan rumus berikut dan hapus tanda komentar pada dua baris terakhir.<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
Seperti yang Anda lihat, kita dapat menggunakan operator ternary untuk memeriksa apakah
`mod()` dari `2.0` berada di bawah `1.0` (baris kedua) atau serupa kita bisa menggunakan `step()` fungsi yang melakukan operasi yang sama, tetapi lebih cepat. Mengapa? Meskipun sulit untuk mengetahui bagaimana setiap kartu grafis mengoptimalkan dan mengkompilasi kodenya, dapat diasumsikan bahwa fungsi built-in lebih cepat daripada fungsi non-built-in. Setiap kali Anda dapat menggunakan fungsi bawaan, gunakanlah!Jadi sekarang setelah kita memiliki rumus angka ganjil, kita dapat menerapkan offset ke baris ganjil untuk memberikan efek bata pada ubin kita. Baris 14 dari kode berikut adalah tempat kita menggunakan fungsi untuk "mendeteksi" baris ganjil dan memberinya offset setengah unit pada
`x`. Perhatikan bahwa untuk baris genap, hasil dari fungsi kita adalah `0.0`, dan mengalikan `0.0` dengan offset `0.5` menghasilkan offset `0.0`. Tapi pada baris ganjil kita mengalikan hasil dari fungsi kita, `1.0`, dengan offset `0.5`, yang menggerakkan sumbu `x` dari sistem koordinat dengan `0,5`.Sekarang coba hapus komentar pada baris 32 - ini memperluas rasio aspek dari sistem koordinat untuk meniru aspek "bata modern". Dengan menghapus komentar pada baris 40 Anda dapat melihat bagaimana sistem koordinat terlihat dipetakan menjadi merah dan hijau.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Coba animasikan ini dengan menggerakkan offset menurut waktu.
* Buat animasi lain di mana baris genap pindah ke kiri dan baris ganjil pindah ke kanan.
* Dapatkah Anda mengulangi efek ini tetapi dengan kolom?
* Coba gabungkan offset pada sumbu
`x` dan `y` untuk mendapatkan sesuatu seperti ini:<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Ubin Truchet
Sekarang setelah kita belajar bagaimana mengetahui apakah sel kita berada di baris atau kolom genap atau ganjil, dimungkinkan untuk menggunakan kembali satu elemen desain tergantung pada posisinya. Pertimbangkan kasus Truchet Tiles di mana satu elemen desain dapat disajikan dalam empat cara berbeda:
Dengan mengubah pola di seluruh ubin, Anda dapat membuat serangkaian desain kompleks yang tak terbatas.
Perhatikan baik-baik fungsi
`rotateTilePattern()`, yang membagi ruang menjadi empat sel dan menetapkan sudut rotasi untuk masing-masing sel.<div class="codeAndCanvas" data="truchet.frag"></div>
* Beri komentar, hapus komentar, dan duplikat baris 69 hingga 72 untuk membuat desain baru.
* Ubah segitiga hitam dan putih untuk elemen lain seperti: setengah lingkaran, kotak atau garis yang diputar.
* Buat kode pola lain di mana elemen diputar sesuai dengan posisinya.
* Buat pola yang mengubah properti lainnya sesuai dengan posisi elemen.
* Pikirkan hal lain yang belum tentu merupakan pola di mana Anda dapat menerapkan asas-asas dari bagian ini. (Contoh: heksagram I Ching)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Membuat aturan Anda sendiri
Membuat pola prosedural adalah latihan mental dalam menemukan elemen minimal yang dapat digunakan kembali. Praktik ini sudah tua; Kita sebagai spesies telah lama menggunakan kisi dan pola untuk menghias tekstil, lantai, dan batas benda: dari pola berkelok-kelok di Yunani kuno, hingga desain kisi Cina, kesenangan akan pengulangan dan variasi menangkap imajinasi kita. Luangkan waktu untuk melihat dekoratif pola dan lihat bagaimana seniman dan desainer memiliki sejarah panjang dalam menavigasi tepi halus antara keteraturan yang dapat diprediksi dan kejutan variasi dan kekacauan. Dari pola geometris Arab, hingga desain kain Afrika yang indah, ada banyak sekali pola yang dapat dipelajari.
Dengan bab ini kami mengakhiri bagian tentang Gambar Algoritmik. Dalam bab-bab berikut kita akan belajar bagaimana membawa beberapa entropi ke shader kita dan menghasilkan desain generatif.
---
09/README It (09/README-it.md)
Motivi
Siccome i programmi shader sono eseguiti pixel per pixel non importa quante volte voi ripetiate una forma, infatti il numero dei calcoli rimane costante. Questo significa che i fragment shader sono particolarmente adatti per creare dei motivi ripetitivi.
In questo capitolo applicheremo ciò che abbiamo imparato fin ad ora e lo ripeteremo all'interno del canvas. Così come nel capitolo precedente, la nostra strategia sarà basata sulla moltiplicazione delle coordinate spaziali (tra 0.0 e 0.1), in modo che le forme che disegniamo tra i valori 0.0 e 1.0 saranno ripetute per creare una griglia.
"The grid provides a framework within which human intuition and invention can operate and that it can subvert. Within the chaos of nature patterns provide a constrast and promise of order. From early patterns on pottery to geometric mosaics in Roman baths, people have long used grids to enhance their lives with decoration." 10 PRINT, Mit Press, (2013)
`fract()`. Questa restituisce la parte frazionale di un numero, facendo in sostanza `fract()` il modulo di uno (`mod(x,1.0)`). In altre parole, `fract()` restituisce il numero dopo la virgola mobile. Il nostro sistema variabile di coordinate (`st`) va già da 0.0 a 1.0, quindi non ha senso fare una cosa simile a:glslvoid main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}
Ma se ingrandiamo il sistema di coordinate, diciamo di tre volte, otterremo tre sequenze di interpolazioni lineari tra 0-1: la prima tra 0-1, la seconda per le virgole mobili tra 1-2 e la terza per le virgole mobili tra 2-3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
È arrivato il momento di disegnare qualcosa in ciascuno sottospazio, togliendo il commento alla riga 27 (Siccome stiamo moltiplicando sia in x sia in y, il rapporto di forma dello spazio non cambia e le forme saranno come previste).
Provate alcuni dei seguenti esercizi per capire meglio:
* Moltiplicate lo spazio per numeri diversi. Provate con valori con la virgola mobile e anche con valori diversi per x e y.
* Create una funzione riutilizzabile con questo trucco di piastrellatura.
* Suddividete lo spazio in 3 righe e 3 colonne. Trovate un modo di sapere in quale colonna e quale riga si trova il thread e usatelo per cambiare la forma che è mostrata. Provate a creare una griglia per il tris.
Applicare le matrici all'interno dei motivi
Siccome ciascuna suddivisione, o cella, è una versione più piccola del sistema normalizzato di coordinate che abbiamo già usato, possiamo applicarle una trasformazione matrice per traslare, ruotare o ridimensionare lo spazio interno.
<div class="codeAndCanvas" data="checks.frag"></div>
* Pensate a modi interessanti di animare questo motivo. Considerate le animazioni di colore, forma e movimento. Fate tre animazioni diverse.
* Riproducete dei motivi più complicati componendo forme diverse.
[](../edit.php#09/diamondtiles.frag)
* Combinate diversi livelli di motivi per comporre il vostro personale motivo di Tartan Scozzese.
Compensare i motivi
Immaginiamo di voler imitare un muro di mattoni. Guardando il muro, potete vedere un offset a mezzo mattone sull'asse x a file alternate. Come possiamo farlo?
Come primo passo dobbiamo sapere se la riga del nostro thread è un numero pari o dispari, perché possiamo usare ciò per determinare se abbiamo bisogno di compensare con un offset le x in quella fila.
____dobbiamo unire i prossimi due paragrafi____
Per determinare se il nostro thread è in una fila pari o dispari, useremo
`mod()` di `2.0` e poi vedremo se il risultato è inferiore a `1.0` o no. Osservate la formula seguente e togliete il commento alle ultime due righe.<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
Come potete vedere, usiamo un operatore ternario per controllare se il
`mod()` di `2.0` sia al di sotto di `1.0` (seconda riga) o potremmo anche usare una funzione `step()` che fa la stessa operazione ma più velocemente. Perché? Nonostante sia difficile sapere come ciascuna carta grafica ottimizza e compila il codice, è sicuro assumere che questa funzione built-in sia più veloce di una non built-in. Ogni volta che potete usare una funzione built-in, fatelo!Ora che abbiamo la formula per il numero dispari, possiamo applicare un offset alle file dispari per dare l'effetto muro di mattoni alla nostra piastrellatura. La riga 14 del codice seguente è dove stiamo usando la funzione per "rilevare" le file dispari e dare loro un offset di mezza unità sulle
`x`. Notate che per le file pari, il risultato della nostra funzione è `0.0`, e moltiplicando `0.0` per l'offset di `0.5`, otteniamo una compensazione di `0.0`; sulle file dispari moltiplichiamo il risultato della nostra funzione, `1.0`, per l'offset di `0.5`, che sposta di `0.5` l'asse delle `x` del sistema.Ora provate a togliere il commento alla riga 32 - questo allunga il rapporto di forma del sistema di coordinate per simulare l'aspetto di un "mattone moderno". Togliendo il commento alla riga 40 potete vedere come il sistema di coordinate appaia mappato con il rosso e il verde.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Provate ad animare questo motivo spostando l'offset in base al tempo.
* Fate un'altra animazione dove le file pari si spostano a sinistra e le file dispari si spostano a destra.
* Riuscite a ripetere quest'effetto con le colonne?
* Provate a combinare un offset sugli assi delle
`x` e delle `y` per ottenere qualcosa di simile a questo:<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Tessere di Truchet
Ora che abbiamo imparato come dire se la nostra cella è in una fila o colonna pari o dispari. È possibile riutilizzare un singolo motivo in relazione alla sua posizione. Considerate il caso delle tessere di Truchet Tiles, dove un singolo motivo può essere rappresentato in quattro modi diversi:
Cambiando il motivo lungo le tessere, è possibile costruire una serie infinita di motivi complessi.
Osservate più da vicino la funzione
`rotateTilePattern()`, che suddivide lo spazio in quattro celle e assegna un angolo di rotazione a ciascuna di esse.<div class="codeAndCanvas" data="truchet.frag"></div>
* Commentate, togliete il commento e duplicate le righe da 69 a 72 per comporre nuovi motivi.
* Cambiate il triangolo bianco e nero con un altro elemento, ad esempio: semicerchi, quadrati o linee ruotati.
* Codificate altri motivi dove gli elementi siano ruotati a seconda della loro posizione.
* Create un motivo che cambi altre proprietà a seconda della posizione degli elementi.
* Immaginate qualcos'altro, che non sia necessariamente un motivo, dove potete applicare i principi visti in questa sezione (es. esagrammi di I Ching).
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Create il vostro insieme di tessere
Creare motivi procedurali è un esercizio mentale nel trovare il più piccole elemento riutilizzabile. Questa è una pratica antica; nel corso della storia abbiamo usato schemi e motivi per decorare tessuti, i fondi e i bordi di oggetti: dai motivi nell'antica Grecia al motivo reticolare cinese, il piacere della ripetizione e della variazione affascina la nostra immaginazione. Prendete del tempo per guardare i motivi decorativi e osservate come artisti e designers nel corso della loro lunga tradizione abbiano saputo navigare tra predicibilità dell'ordine e la sorpresa della variazione e del caos. Dai motivi geometrici arabi, ai bellissimi motivi delle stoffe africane, esiste un intero universo di motivi dal quale imparare.
Con questo capitolo terminiamo la sezione sul disegno algoritmico. Nei prossimi capitoli impareremo a portare dell'entropia nei nostri shader e a produrre motivi generativi.
---
09/README Pl (09/README-pl.md)
Wzory kafelkowe
Ponieważ shadery wykonywane są piksel po pikselu, więc niezależnie od tego, jak często powtarzasz (duplikujesz) dany kształt, liczba obliczeń pozostaje stała. Oznacza to, że fragment shadery nadają się szczególnie do wzorów kafelkowych.
W tym rozdziale zamierzamy zastosować to, czego nauczyliśmy się do tej pory, dodając powtarzalność. Podobnie jak w poprzednich rozdziałach, nasza strategia będzie opierała się na mnożeniu współrzędnych przestrzeni (z przedziału 0.0 a 1.0), dzięki czemu kształty, które narysujemy pomiędzy wartościami 0.0 a 1.0, będą się powtarzać, tworząc siatkę.
Siatka (ang. "grid") zapewnia ramy, w których może działać ludzka intuicja i inwencja, i które może obalić. W chaosie natury wzory zapewniają kontrast i obietnicę porządku. Od wczesnych wzorów na ceramice do geometrycznych mozaik w rzymskich łaźniach, ludzie od dawna używali siatek, by wzbogacić swoje życie o dekoracje. " 10 PRINT, Mit Press, (2013)
fract(). Zwraca ona część ułamkową liczby, dzięki czemu fract() to w istocie funkcja modulo jeden (mod(x,1.0)). Innymi słowy, fract() zwraca liczbę po przecinku. Nasza zmienna znormalizowanego układu współrzędnych (st) już znajduje sie w zakresie od 0.0 do 1.0, więc nie ma sensu robić czegoś takiego jak:glslvoid main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}
Ale jeśli przeskalujemy znormalizowany układ współrzędnych w górę - powiedzmy o trzy - otrzymamy trzy sekwencje interpolacji liniowych między 0-1: pierwszą między 0-1, drugą dla punktów między 1-2 i trzecią dla punktów między 2-3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Teraz nadszedł czas, aby narysować coś w każdej podprzestrzeni, odkomentowując linię 27. (Ponieważ mnożymy x i y po równo, współczynnik proporcji przestrzeni nie zmienia się i kształty będą zgodne z oczekiwaniami).
Spróbuj wykonać niektóre z poniższych ćwiczeń, aby uzyskać głębsze zrozumienie:
* Pomnóż przestrzeń przez różne liczby. Spróbuj z wartościami zmiennoprzecinkowymi, a także z różnymi wartościami dla x i y.
* Zrób funkcję z tej kafelkowej sztuczki, abyś mógł ją ponownie wykorzystać.
* Podziel przestrzeń na 3 wiersze i 3 kolumny. Znajdź sposób identyfikowania, w której kolumnie i rzędzie znajduje się wątek (piksel). Użyj tego, aby zmienić kształt, który jest wyświetlany. Spróbuj stworzyć mecz Kółko i krzyżyk
Macierze wewnątrz kafelków
Ponieważ każdy kafelek jest pomniejszą wersją znormalizowanego układu współrzędnych, którego już używaliśmy, więc możemy zastosować do niego przekształcenie macierzowe w celu translacji, obrotu lub skalowania przestrzeni wewnątrz.
<div class="codeAndCanvas" data="checks.frag"></div>
* Pomyśl o ciekawych sposobach animacji tego wzoru. Rozważ animowanie koloru, kształtu i ruchu. Wykonaj trzy różne animacje.
* Odtwórz bardziej skomplikowane wzory poprzez skomponowanie różnych kształtów.
[](../edit.php#09/diamondtiles.frag)
* Połącz różne warstwy wzorów, aby skomponować własny szkocki tartan.
Przesunięcia kafelków
Powiedzmy, że chcemy odtworzyć mur z cegły. Patrząc na ścianę, można zauważyć przesunięcie pół cegły na osi x w co drugim rzędzie. Jak możemy to zrobić?
Jako pierwszy krok musimy wiedzieć, czy rząd naszego wątku (piksela) jest liczbą parzystą czy nieparzystą, co pomoże określić, czy musimy przesunąć x w tym rzędzie. W tym celu użyjemy
mod() z 2.0, a następnie zobaczymy, czy wynik jest mniejszy niż 1.0 czy nie. Spójrz na poniższy kod i odkomentuj dwie ostatnie linie.<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
Jak widać możemy użyć operatora warunkowego do sprawdzenia czy
mod() z 2.0 jest mniejszy od 1.0 (druga linia) lub podobnie możemy użyć funkcji step(), która wykonuje tę samą operację, ale szybciej. Dlaczego? Chociaż trudno jest wiedzieć jak każda karta graficzna optymalizuje i kompiluje kod, to można bezpiecznie założyć, że funkcje wbudowane są szybsze od tych niewbudowanych. Zawsze, gdy możesz użyć wbudowanej funkcji, użyj jej!Korzystając z powyższej metody wykrywania liczb nieparzystych, możemy przesunąć nieparzyste rzędy, aby upodobnić naszą kawnę do ceglanej ściany. W linii 14 poniższego kodu identyfikujemy parzystość rzędów i nadajemy im przesunięcie na
x. Zauważ, że dla parzystych rzędów, wynikiem obliczeń w step() jest 0.0, co, mnożąc przez 0.5, daje przesunięcie 0.0. Natomiast w nieparzystych rzędach mnożymy 1.0 przez 0.5, co powoduje przesunięcie osi x układu współrzędnych o 0.5.Teraz spróbuj odkomentować linię 32 - rozciąga ona proporcje układu współrzędnych, aby odtworzyć wymiary "nowoczesnej cegły". Odkomentowując linię 40 możesz zobaczyć jak wygląda układ współrzędnych zmapowany na kolor czerwony i zielony.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Zanimuj ten przykład, zwiększając przesunięcie w zależności od czasu.
* Zrób kolejną animację, w której parzyste wiersze przesuwają się w lewo, a nieparzyste w prawo.
* Czy możesz powtórzyć ten efekt, ale z kolumnami?
* Spróbuj połączyć przesunięcie na osi
x i y, aby uzyskać coś takiego:<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Kafelki Truchet'a
Teraz, gdy dowiedzieliśmy się, jak określić, czy nasza komórka znajduje się w parzystym czy nieparzystym wierszu lub kolumnie, możliwe jest ponowne wykorzystanie pojedynczego elementu projektu w zależności od jego pozycji. Rozważmy przypadek kafelków Truchet'a, gdzie pojedynczy kafelek może być przedstawiony na cztery różne sposoby:
Zmieniając wzór na kafelkach, można zbudować nieskończony zestaw skomplikowanych wzorów.
Zwróć uwagę na funkcję
rotateTilePattern(), która dzieli przestrzeń na cztery komórki i każdej z nich przypisuje kąt obrotu.<div class="codeAndCanvas" data="truchet.frag"></div>
* Zakomentowywuj, odkomentowywuj i powielaj linie od 69 do 72, aby komponować nowe wzory.
* Zmień czarno-biały trójkąt na inny element, taki jak: półkola, obrócone kwadraty lub linie.
* Zakoduj inne wzory, w których elementy są obracane w zależności od ich położenia.
* Zrób wzór, który zmienia inne właściwości w zależności od położenia elementów.
* Pomyśl o czymś innym, co niekoniecznie jest wzorem, gdzie możesz zastosować zasady z tego działu. (Na przykład: heksagramy I Ching)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Tworzenie własnych reguł
Tworzenie wzorców proceduralnych to ćwiczenie umysłowe polegające na znajdowaniu minimalnych elementów wielokrotnego użytku. Praktyka ta jest stara; my jako gatunek od dawna używamy siatek i wzorów do dekorowania tkanin, podłóg i obramowań obiektów: od meandrowych wzorów w starożytnej Grecji, po chińskie wzory kratowe, przyjemność z powtórzeń i wariacji przykuwa naszą uwagę i pobudza wyobraźnię. Poświęć trochę czasu, aby spojrzeć na dekoracyjne wzory i zobacz długą historię tego, jak artyści i projektanci poruszają się po cienkiej krawędzi między przewidywalnością porządku a niespodzianką zmienności i chaosu. Od arabskich wzorów geometrycznych do wspaniałych afrykańskich wzorów tkanin, istnieje cały wszechświat wzorów, z których można się uczyć.
Tym rozdziałem kończymy część poświęconą Rysowaniu Algorytmicznemu. W kolejnych rozdziałach dowiemy się, jak wprowadzić trochę entropii do naszych shaderów, tworząc generatywny design.
---
09/README Pt (09/README-pt.md)
Patterns (padrões)
Uma vez que os programas de shaders são executados pixel a pixel, não importa o quanto você repete uma forma, o número de cálculos permanece constante. Isso quer dizer que fragment shaders se encaixam perfeitamente para a criação de padrões "tile patterns".
Neste capítulo, vamos aplicar o que aprendemos até agora, e repetir isso ao longo da tela. Como nos capítulos anteriores, nossa estratégia será baseada em multiplicar as coordenadas espaciais (entre 0.0 e 1.0), de modo que as formas que desenharmos entre os valores 0.0 e 1.0 serão repetidas para fazer um grid.
"O grid provê uma framework na qual a intuição e a invenção humana podem operar e que pode subverter. Dentro do caos da natureza, os padrões provêem um contraste e promessa de ordem. Desde padrões mais antigos nas cerâmicas até mosaicos geométricos nos banheiros de Roma, as pessoas têm usado grids para melhorar suas vidas com a decoração." 10 PRINT, Mit Press, (2013)
`fract()`. Ela retorna a parte fracionária de um número, tornando `fract()`, em essência, o módulo de um (`mod(x,1.0)`). Em outras palavras, `fract()` retorna o número depois do ponto flutuante. Nossa variável do sistema de coordenadas normalizadas (`st`) já vai de 0.0 a 1.0, então não faz sentido termos algo como:glslvoid main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}
Mas se nós escalarmos o sistema de coordenadas para cima - digamos, aumentar por três - vamos ter três sequências de interpolações lineares entre 0-1: a primeira, entre 0-1, a segunda para os pontos flutuantes entre 1-2, e a terceira para os pontos flutuantes entre 2-3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Agora é horas de desenhar algo em cada subespaço, descomentando a linha 27 (devido ao fato de estarmos multiplicando igualmente os valores de x e y, o aspecto do espaço não muda e as formas serão como esperado).
Tente os seguintes exercícios para entender mais a fundo:
* Multiplique o espaço por números diferentes. Tente com valores de ponto flutuante e também com valores diferentes para x e y.
* Crie uma função reutilizável para esse truque com os padrões.
* Divida o espaço em três linhas e três colunas. Ache um jeito de saber em que coluna e linha está a thread, e use isso para mudar a forma que está exibindo. tente compor um jogo da velha.
Aplique matrizes dentro dos patterns
Como cada subdivisão ou célula é uma versão menor do sistema de coordenadas normalizadas que já vínhamos usando, podemos aplicar uma transformação de matrizes a ela, de modo a transladar, rotacionar ou escalar o espaço ali dentro.
<div class="codeAndCanvas" data="checks.frag"></div>
* Pense em modos interessantes de animar esse padrão. Considere animação de cores, formas e movimento. Faça três animações diferentes.
* Recrie patterns mais complicados, compondo formas diferentes.
[](../edit.php#09/diamondtiles.frag)
* Combine camadas diferentes de patterns, para compor seus próprios Scottish Tartan Patterns.
Offset patterns
Então, digamos que queremos imitar um muro de tijolos. Olhando para o muro, você pode ver um deslocamento de meio tijolo em x, entre as linhas. Como podemos fazer isso?
Como primeiro passo, precisamos saber se a linha de nossa thread é uma linha par ou ímpar, porque podemos usar isso a para determinar se precisamos fazer o deslocamento do x nessa linha.
____nós temos que consertar esses dois próximos parágrafos, juntos____
Para determinar se nossa thread está numa linha par ou ímpar, vamos usar o
`mod()` de `2.0` e então ver se se o resultado está abaixo de `1.0` ou não. Dê uma olhada na fórmula seguinte e descomente as duas últimas linhas.<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
Como pode ver, usamos um operador ternário para checar se o
`mod()` de `2.0` é abaixo de `1.0` (segunda linha) ou de modo similar, podemos usar um `step()` que faz a mesma operação, mas mais rápido. Por quê? Embora seja difícil saber como cada placa gráfica otimiza e compila o programa, é seguro assumir que funções built-in são mais rápidas que as outras. Toda vez que puder usar uma função built-in , use!Então, agora que temos nossa fórmula para números ímpares, podemos aplicar um deslocamento nas linhas ímpares, para dar um efeito de tijolos aos nossos "tiles". A linha 14 do código a seguir é onde estamos usando a função para "detectar" linhas ímpares e dar a elas um offset de meia unidade em
`x`. Note que para linhas pares, o resultado da função é `0.0`, e multiplicar `0.0` pelo offset de `0.5` nos dá um offset de `0.0`. Mas em linhas ímpares, multiplicamos o resultado da função, `1.0`, pelo offset de `0.5`, o que move o eixo `x` do sistema de coordenadas por `0.5`.Agora, tente descomentar a linha 32 - isso estica a proporção da tela do sistema de coordenadas, para imitar o aspecto de um "tijolo moderno". Comentando a linha 40, você pode ver como o sistema de coordenadas parece mapeado para o vermelho e o verde.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Tente animar isso, movendo o offset de acordo com o tempo.
* Faça outra animação onde as linhas pares se movem para a esquerda, e as ímpares para a direita.
* Você consegue repetir esse efeito, mas com colunas?
* Tente combinar um offset nos eixos
`x` e `y` para conseguir algo desse tipo:<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Tiles de Truchet
Agora que já aprendemos como dizer se nossa célula está numa coluna par ou ímpar, é possível reusar um elemento simples dependendo de sua posição. Considere o caso dos Truchet Tiles onde um elemento de design simples pode ser representado em quatro formas diferentes:
Mudando o padrão ao longo dos tiles, é possível construir um conjunto infinito de designs complexos.
Preste bastante atenção à função
`rotateTilePattern()`, que subdivide o espaço em quatro célular e associa uma ângulo de rotação a cada um.<div class="codeAndCanvas" data="truchet.frag"></div>
* Comente, descomente e duplique as linhas 69 a 72 para compor novos designs.
* Mude o triângulo preto e branco por outro elemento, como: semi-círculos, quadrados rotacionados ou linhas.
* Programe outros padrões, onde os elementos são rotacionados de acordo com a posição.
* Faça um pattern que mude outras propriedades, de acordo com a posição dos elementos.
* Pense em algo mais que não seja necessariamente um pattern onde você pode aplicar os princípios desta seção. (Ex: hexagramas do I Ching)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Fazendo suas próprias regras
Fazer padrões procedurais é um exercício mentar para encontrar elementos mínimos reutilizáveis. Esta prática é antiga; como espécie, temos usando grids e padrões para decorar tecidos, pisos e bordas de objetos por muito tempo: desde os padrões de meandros na Grécia antiga, aos designs de treliças chinesas, o prazer da repetição e variações pegam nossa imaginação. Dê uma olhada em padrões decorativos e veja como artistas e designers têm uma longa história de navegar na fina borda entre a previsibilidade da ordem, e a surpresa da variação e o caos. Dos padrões geométricos árabes ao design de lindos tecidos africanos, há um universo inteiro de padrões de onde aprender.
Com este capítulo, terminamos a seção sobre Desenho Algorítmico. Nos capítulos seguintes vamos aprender como trazer alguma entropia para nossos shaders e produzir designs generativos.
---
09/README Ru (09/README-ru.md)
Узоры
Шейдерные программы исполняются попиксельно, поэтому вне зависимости от количества повторений фигуры объём вычислений не изменяется. Это значит, что фрагментные шейдеры хорошо справляются с повторяющимися узорами.
В этой главе мы собираемся применить весь ранее изученный материал, и повторить это в изображении несколько раз. Как и в предыдущих главах, наша стратегия будет основана на умножении пространственных координат (между 0.0 и 1.0), так чтобы фигуры, которые мы рисуем между 0.0 и 1.0 повторялись несколько раз, образуя решётку.
«Регулярная решётка - это то, с чем человеческой интуиции и изобретательности проще всего работать. Повторяющиеся элементы вступают в контраст с хаосом мироздания и создают ощущение порядка. Люди всегда старались украсить и разнообразить окружающее пространство с помощю повторяющихся элементов. Это прослеживается от доисторических узоров на керамике до геометрических мозаик римских бань.» 10 PRINT, Издательство MIT, (2013)
`fract()`. Она возвращает дробную часть числа, то есть работает как взятие остатка от деления на единицу (`mod(x,1.0)`). Другими словами, `fract()` возвращает число справа от точки. Переменная с нормализованными координатами (`st`) уже пробегает значения от 0.0 до 1.0, поэтому нет смысла делать что-то вроде:glslvoid main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}
Но если мы увеличим масштаб нормализованной системы координат, скажем, в три раза, то получится три отрезка линейной интерполяции между 0.0 и 1.0: между 0 и 1, между 1 и 2, и наконец между 2 и 3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Теперь давайте нарисуем что-нибудь в каждом подпространстве, раскомментировав строку 27. Соотношение сторон при этом не изменится и фигуры не будут искажены, ибо мы умножаем по обеим осям.
Для более полного понимания попробуйте выполнить следующее:
* Поумножайте пространственные координаты на различные числа. Поэкспериментируйте с дробными числами и с различными множителями для x и y.
* Сделайте функцию создания узоров, пригодную для повторного использования.
* Разделите пространство на 3 строки и 3 столбца. Придумайте как узнать в какой строке и каком столбце находится текущий поток, и изменяйте фигуру в зависимости от этого. Нарисуйте игру в крестики-нолики.
Применение матриц к узорам
Каждая клетка решётки является уменьшенной версией нормализованной системы координат, которую мы использовали ранее, поэтому мы можем применить к этим клеткам матричные преобразования переноса, поворота и масштаба.
<div class="codeAndCanvas" data="checks.frag"></div>
* Придумайте интересные анимации для этого узора. Анимируйте цвет, форму и движение. Сделайте три различных анимации.
* Создайте более сложные узоры, совмещая разные формы.
[](../edit.php#09/diamondtiles.frag)
* Создайте узор шотландского тартана, совмещая узоры в несколько слоёв.
Узоры со сдвигом
Давайте сымитируем кирпичную стену. Каждый ряд кирпичей в стене смещён на полкирпича по оси x относительно предыдущего ряда. Как это сделать?
Для начала нам нужно узнать чётность номера строки, над которой работает данный поток. Таким образом мы сможем понять, нужно ли делать сдвиг по x в этой строке.
Чтобы определить чётность строки, используем взятие по модулю 2.0 с помощью функции
`mod()`, и сравним результат с единицей. Посмотрите на формулы ниже и раскомментируйте две последние строки.<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
Как видите, мы могли бы использовать тернарный оператор для сравнения значения по модулю
`2.0` с единицей, но того же эффекта можно достичь с помощью `step()`, которая работает быстрее. Почему? Хотя мы и не знаем как графические карты оптимизирует код, безопаснее будет предположить что встроенные функции работает быстрее, чем не встроенные. Если у вас есть возможность использовать встроенную функцию - используйте!Теперь у нас есть формула вычисления чётности и мы можем сдвинуть нечётные строки для создания эффекта кирпичной стены. В 14 строке следующего кода мы используем эту формулу для «обнаружения» нечётных строк. Как видите, для чётных строк функция возвращает
`0.0`, что при умножении на сдвиг `0.5` так же даёт `0.0`, а значит чётные строки не сдвигаются. В нечётных же строках `0.5` умножается на `1.0`, поэтому в них пространство сдвигается на `0.5` по оси x.Теперь попробуйте раскомментировать строку 32. Это сделает соотношение сторон похожим на соотношение сторон кирпича. А раскомментировав 40 строку вы увидите отображение координат в красный и зелёный цвета.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Попробуйте анимировать этот пример, изменяя сдвиг в зависимости от времени.
* Создайте ещё одну анимацию, где чётные строки движутся налево, а нечётные - направо.
* Можете ли вы повторить такой же эффект для столбцов?
* Сделайте сдвиг по x и y одновременно, чтобы получить что-то вроде этого:
<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Плитка Труше
Теперь, когда мы научились определять чётность строки и столбца для каждой клетки, мы можем многократно использовать один и тот же элемент в зависимости от его расположения. Рассмотрим плитку Труше, где один элемент дизайна может быть представлен четырьмя различными способами:
Изменяя рисунок в зависимости от расположения плитки, можно создать бесконечно много сложных изображений.
Обратите особое внимание на функцию
`rotateTilePattern()`, которая разделяет пространство на четыре части и задаёт угол поворота каждой из них.<div class="codeAndCanvas" data="truchet.frag"></div>
* Закомментируйте, раскомментируйте и продублируйте строки с 69 по 72, чтобы скомпоновать новые изображения.
* Замените чёрно-белый треугольник на какой-нибудь другой элемент, например полукруг, повернутые квадраты или линии.
* Напишите другие узоры с элементами, повёрнутыми в зависимости от их расположения.
* Создайте узор, элементы которого меняют другие свойства в зависимости от расположения.
* Придумайте ещё какое-нибудь изображение (не обязательно повторяющийся узор), в котором можно применить принципы из этой главы, например, Гексаграммы И цзин).
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Создавайте свои собственные правила
Создание процедурных узоров - это умственное упражнение по поиску минимальных повторно используемых элементов. Это древняя практика. Мы, как биологический вид, издавна использовали узоры для украшения тканей, пола и границ объектов: вспомните извилистые узоры древней Греции или решётчатые узоры из Китая. Магия повторений и вариаций всегда захватывала наше воображение. Рассмотрите декоративные узоры и обратите внимание как художники и дизайнеры балансируют между предсказуемостью порядка и внезапностью изменчивости хаоса. От арабских геометрических узоров и до бесподобных африканских тканей раскинулась целая вселенная узоров, среди которых есть что изучить.
Этой главой мы заканчиваем раздел об алгоритмическом рисовании. В следующих главах мы привнесём в шейдеры немного энтропии для создания генеративного дизайна.
---
09/README Tr (09/README-tr.md)
Desenler
Shader programları piksel piksel çalıştırıldığından, bir şekli ne kadar tekrarlarsanız tekrarlayın hesaplama sayısı sabit kalır. Bu, fragment shader'larını döşeme desenleri için özellikle uygun kılar.
Bu bölümde şimdiye kadar öğrendiklerimizi uygulayacak ve tuval boyunca tekrarlayacağız. Önceki bölümlerde olduğu gibi, stratejimiz uzay koordinatlarını (0.0 ile 1.0 arasında) çarpmaya dayanacaktır; böylece 0.0 ile 1.0 arasında çizdiğimiz şekiller bir ızgara oluşturmak üzere tekrarlanacaktır.
"Izgara, insan sezgisi ve icadının çalışabileceği ve altüst edebileceği bir çerçeve sağlar. Doğanın kaosunun içinde desenler bir karşıtlık ve düzen vaadi sunar. Çömleklerdeki erken desenlerden Roma hamamlarındaki geometrik mozaiklere kadar, insanlar yaşamlarını süsleme ile zenginleştirmek için uzun süredir ızgaralar kullanmaktadır." 10 PRINT, Mit Press, (2013)
`fract()` fonksiyonunu hatırlayalım. Bir sayının kesir kısmını döndürür, yani `fract()` özünde birin modülosudur (`mod(x,1.0)`). Başka bir deyişle, `fract()` ondalık noktadan sonraki sayıyı döndürür. Normalize edilmiş koordinat sistemi değişkenimiz (`st`) zaten 0.0'dan 1.0'a gittiğinden şunu yapmak mantıklı olmaz:glslvoid main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}
Ama normalize edilmiş koordinat sistemini yukarı ölçeklersek — diyelim ki üçle — 0-1 arasında üç doğrusal enterpolasyon dizisi elde ederiz: birincisi 0-1 arasında, ikincisi 1-2 arasındaki kayan noktalar için ve üçüncüsü 2-3 arasındaki kayan noktalar için.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Şimdi 27. satırın yorumunu kaldırarak her alt uzayda bir şeyler çizme zamanı. (x ve y'de eşit olarak çarptığımız için uzayın en-boy oranı değişmez ve şekiller beklendiği gibi olur.)
Daha derin bir anlayış elde etmek için aşağıdaki alıştırmalardan bazılarını deneyin:
* Uzayı farklı sayılarla çarpın. Kayan nokta değerleriyle ve ayrıca x ve y için farklı değerlerle deneyin.
* Bu döşeme numarasından yeniden kullanılabilir bir fonksiyon yapın.
* Uzayı 3 satır ve 3 sütuna bölün. İş parçacığının hangi sütun ve satırda olduğunu bilmenin bir yolunu bulun ve bunu görüntülenen şekli değiştirmek için kullanın. Bir tic-tac-toe maçı oluşturmaya çalışın.
Desenler içinde matris uygulama
Her alt bölüm veya hücre, daha önce kullandığımız normalize edilmiş koordinat sisteminin daha küçük bir versiyonu olduğundan, uzayı içeride ötelemek, döndürmek veya ölçeklemek için bir matris dönüşümü uygulayabiliriz.
<div class="codeAndCanvas" data="checks.frag"></div>
* Bu deseni canlandırmanın ilginç yollarını düşünün. Rengi, şekilleri ve hareketi canlandırmayı düşünün. Üç farklı animasyon yapın.
* Farklı şekilleri birleştirerek daha karmaşık desenler yeniden oluşturun.
[](../edit.php#09/diamondtiles.frag)
* Kendi İskoç Tartan Desenlerinizi oluşturmak için farklı desen katmanlarını birleştirin.
Ofset desenler
Diyelim ki bir tuğla duvarı taklit etmek istiyoruz. Duvara bakıldığında, her diğer satırda x'te yarım tuğla ofseti görülebilir. Bunu nasıl yapabiliriz?
İlk adım olarak iş parçacığımızın satırının çift mi yoksa tek sayı mı olduğunu bilmemiz gerekir, çünkü bunu o satırda x'i ofsetlememiz gerekip gerekmediğini belirlemek için kullanabiliriz. Bunun için
`mod()` of `2.0`'ı kullanacak ve sonucun `1.0`'ın altında olup olmadığına bakacağız. Aşağıdaki formüle bakın ve son iki satırın yorumunu kaldırın.<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
`mod()` of `2.0`'ın `1.0`'ın altında olup olmadığını kontrol etmek için bir üçlü operatör kullanabiliriz (ikinci satır) veya benzer şekilde aynı işlemi yapan ama daha hızlı olan `step()` fonksiyonunu kullanabiliriz. Neden? Her grafik kartının kodu nasıl optimize ettiğini ve derlediğini bilmek zor olsa da, yerleşik fonksiyonların yerleşik olmayanlardan daha hızlı olduğunu varsaymak güvenlidir. Bir yerleşik fonksiyon kullanabildiğiniz her yerde, kullanın!Artık tek sayı formülümüz olduğuna göre, döşemelerimize tuğla efekti vermek için tek satırlara bir ofset uygulayabiliriz. Aşağıdaki kodun 14. satırı, fonksiyonu tek satırları "algılamak" ve onlara
`x`'te yarım birim ofset vermek için kullandığımız yerdir. Çift satırlar için fonksiyonumuzun sonucunun `0.0` olduğunu ve `0.0`'ın `0.5` ofsetiyle çarpılmasının `0.0` ofset verdiğini not edin. Ancak tek satırlarda fonksiyonumuzun sonucunu, `1.0`'ı, `0.5` ofsetiyle çarpıyoruz, bu da koordinat sisteminin `x` eksenini `0.5` kadar hareket ettiriyor.Şimdi 32. satırın yorumunu kaldırmayı deneyin — bu, "modern tuğla" görünümünü taklit etmek için koordinat sisteminin en-boy oranını uzatır. 40. satırın yorumunu kaldırarak koordinat sisteminin kırmızı ve yeşile nasıl eşlendiğini görebilirsiniz.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Ofseti zamana göre hareket ettirerek bunu canlandırmayı deneyin.
* Çift satırların sola ve tek satırların sağa hareket ettiği başka bir animasyon yapın.
* Bu efekti sütunlarla tekrarlayabilir misiniz?
* Şöyle bir şey elde etmek için
`x` ve `y` ekseninde bir ofseti birleştirmeyi deneyin:<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Truchet Döşemeleri
Artık hücremizin çift mi yoksa tek satır veya sütunda mı olduğunu söylemeyi öğrendiğimize göre, konumuna bağlı olarak tek bir tasarım öğesini yeniden kullanmak mümkündür. Tek bir tasarım öğesinin dört farklı şekilde sunulabildiği Truchet Döşemeleri durumunu düşünün:
Döşemeler arasında deseni değiştirerek, sonsuz sayıda karmaşık tasarım oluşturmak mümkündür.
`rotateTilePattern()` fonksiyonuna yakından bakın; uzayı dört hücreye böler ve her birine bir dönme açısı atar.<div class="codeAndCanvas" data="truchet.frag"></div>
* Yeni tasarımlar oluşturmak için 69-72 arası satırları yorum yapın, yorumdan çıkarın ve çoğaltın.
* Siyah beyaz üçgeni başka bir öğeyle değiştirin: yarım daireler, döndürülmüş kareler veya çizgiler gibi.
* Öğelerin konumlarına göre döndürüldüğü diğer desenleri kodlayın.
* Öğelerin konumuna göre diğer özellikleri değiştiren bir desen yapın.
* Bu bölümdeki prensipleri uygulayabileceğiniz, mutlaka bir desen olmayan başka bir şey düşünün. (Ör: I Ching hegzagramları)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Kendi kurallarınızı oluşturma
Prosedürel desenler yapmak, minimal yeniden kullanılabilir öğeler bulmaya yönelik zihinsel bir egzersizdir. Bu pratik eskidir; bir tür olarak tekstilleri, zeminleri ve nesnelerin kenarlarını süslemek için uzun süredir ızgaralar ve desenler kullanıyoruz: antik Yunan'daki meander desenlerinden Çin kafes tasarımlarına kadar, tekrarlama ve çeşitlemenin verdiği zevk hayal gücümüzü yakalar. Dekoratif desenlere bakarak zaman ayırın ve sanatçılar ile tasarımcıların düzenin öngörülebilirliği ile çeşitleme ve kaosun sürprizi arasındaki ince çizgide uzun bir gezinme geçmişine sahip olduğunu görün. Arap geometrik desenlerinden muhteşem Afrika kumaş tasarımlarına kadar, öğrenilecek kocaman bir desenler evreni var.
Bu bölümle Algoritmik Çizim bölümünü bitiriyoruz. Sonraki bölümlerde shader'larımıza biraz entropi getirmeyi ve üretken tasarımlar üretmeyi öğreneceğiz.
---
09/README Vi (09/README-vi.md)
Mẫu hoa văn
Vì các chương trình shader được tính toán cho từng pixel nên dù bạn có vẽ một hình bao nhiêu lần thì cũng không ảnh hưởng tới số phép tính. Điều đó đặc biệt thích hợp nếu ta dùng shader để vẽ các mẫu hoa văn hoạ tiết lặp lại.
Ở chương này ta sẽ dùng hết tất cả những gì đã học để vẽ rồi lặp lại hình vẽ đó ra toàn bộ canvas. Cũng giống như các chương trước, ta sẽ vẽ hình trong một khu vực khoảng [0.0, 1.0] trên hệ toạ độ, rồi tạo bản sao của các khu vực đó để tạo nên một lưới các hình vẽ lặp lại.
"Hệ thống lưới tạo điều kiện để loài người khám phá và phát minh. Trong thiên nhiên hoang dã, các hoa văn giúp ta nhận biết dễ hơn. Người xưa đã dùng các hoa văn từ rất sớm để trang trí, từ các hoạ tiết trên đồ gốm tới các hoạ tiết khảm hình học trong các nhà tắm La Mã." 10 PRINT, Mit Press, (2013)
`fract()`. Nó trả về phần thập phân của số thực, tương đương với phép tính phần dư khi chia cho 1 (`mod(x,1.0)`). Nói cách khác, hàm `fract()` trả về phần lẻ sau dấu thập phân. Hệ toạ độ của ta thì đã nằm trong khoảng [0.0, 1.0] rồi nên dùng hàm `fract()` trong khoảng này chẳng có ý nghĩa gì:glslvoid main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st,0.0);
gl_FragColor = vec4(color,1.0);
}
Nhưng nếu ta tăng kích thước của hệ toạ độ lên - gấp 3 lần chẳng hạn - thì cũng với đoạn code trên, ta sẽ thu được 3 đoạn biến thiên tuyến tính giống nhau: đoạn đầu tiên nằm trong khoảng 0-1, đoạn thứ hai nằm trong khoảng 1-2 và đoạn cuối nằm trong khoảng 2-3.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Vậy là ta đã có 1 lưới 3x3 rồi, hãy coi mỗi ô là một không gian thu nhỏ. Bạn hãy uncomment dòng 27 để vẽ một hình gì đó ở mỗi không gian thu nhỏ nhé. (Do ta đã giãn cả 2 trục x và y với cùng 1 tỉ lệ nên các hình vẽ sẽ không bị bóp méo gì cả). Chú ý: Đoạn code không hề dùng vòng lặp.
Hãy thử sửa code như sau để hiểu rõ hơn:
* Giãn 2 trục x và y với 1 tỉ lệ khác, thậm chí là số thực và mỗi trục 1 tỉ lệ khác nhau.
* Tạo 1 hàm để sử dụng lại sau này cho mẹo lặp hoạ tiết này.
* Chia lưới thành 3 hàng và 3 cột. Tìm cách nhận biết từng hàng và cột đang được xử lý để có thể tuỳ ý thay đổi hình vẽ trong mỗi ô. Thử vẽ một bàn cờ caro xem sao.
Apply matrices inside patterns
Vì mỗi ô là một hệ toạ độ thu nhỏ, ta có thể áp dụng các phép biến đổi ma trận để di chuyển, quay và thu phóng từng ô.
<div class="codeAndCanvas" data="checks.frag"></div>
* Hãy nghĩ cách làm cho hoạ tiết này chuyển động. Thử thay đổi màu sắc, hình dáng và cả chuyển động xem sao. Hãy tạo 3 animation riêng biệt.
* Tạo các hoạ tiết phức tạp hơn bằng cách kết hợp nhiều hình cơ bản.
[](../edit.php#09/diamondtiles.frag)
* Hãy kết hợp nhiều lớp hoạ tiết chồng lên nhau để tạo nên hoạ tiết kẻ ca-rô Tartan đặc trưng của Scotland.
Hoạ tiết so le
Giả sử ta muốn tạo bên một bức tường gạch. Hãy nhìn ảnh dưới đây và bạn sẽ thấy các hàng gạch được xếp so le với nhau. Làm thế nào ta có thể vẽ được hình đó ?
Đầu tiên ta cần xác định được tính chẵn lẻ của mỗi hàng, thì mới biết có cần dịch chuyển hàng đó để tạo nên hoạ tiết so le không.
Để biết hàng đang vẽ là chẵn hay lẻ, ta sẽ dùng hàm tính phần dư (
`mod()`) khi chia cho `2.0`. Nếu là phép chia hết thì đó là số chẵn, ngược lại là số lẻ. Hãy uncomment 2 dòng cuối của đoạn code dưới đây.<div class="simpleFunction" data="y = mod(x,2.0);
// y = mod(x,2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0,mod(x,2.0));"></div>
Bạn có thể thấy tôi đã dùng toán tử ba ngôi (ternary) để kiểm tra xem hàm
`mod()` khi chia cho `2.0` thì phần dư có nhỏ hơn `1.0` không. Dòng cuối cùng, thì tôi chọn cách khác mà vẫn cho ra kết quả tương tự, đó là dùng hàm `step()`, cách này nhanh hơn. Tại sao lại thế ? Thực ra thì khó mà biết được card đồ hoạ biên dịch và tối ưu code ra sao, nhưng nhiều khả năng là các hàm có sẵn sẽ nhanh hơn các hàm ta tự viết. Còn trong trường hợp này thì step() nhanh hơn vì nó được tối ưu bằng phần cứng, còn các lệnh điều kiện thì rất chậm trên các card đồ hoạ. Vì thế nếu có thể hãy cố gắng dùng các hàm có sẵn và hạn chế dùng các lệnh điều kiện.Giờ thì ta có công thức tìm ra các hàng lẻ rồi nên chỉ cần dịch chuyển các viên gạch một nửa đơn vị theo trục
`x` trên những hàng này thì sẽ tạo nên hoạ tiết so le thôi. Dòng 14 trong đoạn code dưới đây thực hiện đúng như vậy. Tuy nhìn qua thì thấy ta cũng áp dụng dịch chuyển cho cả các hàng chẵn nữa, nhưng vì kết quả phép tính phần dư bằng `0` cho các hàng này nên thành ra các viên gạch không bị dịch chuyển chút nào và vẫn giữ nguyên vị trí như ta mong muốn.Hãy thử uncomment dòng 32 nhé, đó là dòng code tôi dùng để kéo giãn 2 trục theo tỉ lệ khác nhau để tạo nên hình viên gạch. Uncomment tiếp dòng 40, bạn sẽ thấy hệ trục toạ độ của mỗi ô lưới được minh hoạ bằng màu đỏ và xanh lá.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Hãy thử làm các hàng gạch chuyển động theo thời gian nhé
* Hãy chuyển động các hàng chẵn sang bên trái còn các hàng lẻ sang bên phải thử xem.
* Nếu phải chuyển sang dịch các cột so le nhau thì bạn làm được không ?
* Kết hợp dịch chuyển cả 2 trục toạ độ để tạo nên chuyển động như dưới đây:
<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
Hoạ tiết Truchet
Ta có thể nhận biết từng ô ở hàng chẵn hay lẻ, ở cột chẵn hay lẻ, nên có thể tuỳ ý xử lý hình vẽ mỗi ô dựa vào tính chẵn lẻ này. Hoạ tiết Truchet chỉ cần 1 hình vẽ nhưng chỉ cần quay 90 độ là có thêm phiên bản nữa.
Bằng cách quay từng ô, ta có thể tạo nên vô số thiết kế khác nhau.
Hãy chú ý vào hàm
`rotateTilePattern()` dưới đây, nó chia mỗi ô thành 4 ô nhỏ hơn để chứa cả 4 phiên bản của hoạ tiết Truchet.<div class="codeAndCanvas" data="truchet.frag"></div>
* Hãy comment, uncomment và tạo bản sao của các dòng 69 và 72 để tạo nên các thiết kế mới.
* Hãy thay hoạ tiết gốc gồm 2 hình tam giác đen trắng bằng các hoạ tiết khác như: 2 nửa hình tròn, hình thoi, đường thẳng.
* Tạo nên các hoạ tiết mà mỗi ô xác định góc xoay dựa vào vị trí của chính ô đó
* Tạo nên các hoạ tiết có các thuộc tính khác nhau dựa vào vị trí của chính ô đó
* Áp dụng các kỹ thuật học được ở chương này để tạo ra thứ gì đó không phải hoạ tiết (Ví dụ: quẻ bói Kinh Dịch)
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
Tự thiết kế
Tạo nên các hoạ tiết lặp lại là một bài tập tốt cho trí não khi phải tìm cách tái sử dụng một số lượng rất nhỏ các hoạ tiết ban đầu. Con người đã dùng kỹ thuật này từ rất lâu rồi, từ việc trang trí hoạ tiết trên những tấm vải, cho tới các sàn gạch được lát tỉ mỉ; từ các đường viền Hy Lạp cổ tới các thiết kế cửa Trung Quốc, sự hoà quyện của các hoạ tiết dù lặp lại mà vẫn đa dạng kích thích trí tưởng tượng của con người. Hãy dành chút thời gian để ngắm nhìn các hoạ tiết trang trí mà các hoạ sỹ đã sử dụng để che giấu phần lặp lại và làm ta ngạc nhiên với những sự sắp đặt đầy bất ngờ. Từ các hoạ tiết hình học Ả Rập, tới các hoa văn trên vải của Châu Phi, có cả một vũ trụ các hoa văn hoạ tiết mà ta có thể khám phá và học hỏi.
Đây cũng là chương cuối của phần Các thuật toán vẽ hình. Ở các chương tiếp theo ta sẽ học cách đưa một ít xáo trộn từ entropy vào shader để tạo nên những hình ảnh độc nhất, không thể đoán trước.
---
09/SUMMARY Pt (09/SUMMARY-pt.md)
Padrões repetitivos são um tema perfeito para desenho computacional. Diferente do modo convencional de desenhar, shaders te deixam desenhar tudo em paralelo de uma vez. Em vez de repetir o mesmo procedimento várias vezes, você vai empacotar e repetir o "espaço". Parece sci-fi? Vamos descobrir o que isso quer dizer de verdade.
---
09/SUMMARY (09/SUMMARY.md)
Repetitive patterns are perfect theme for computational sketching. Different from conventional way of drawing, shaders lets you draw everything parallelly at once. Instead of repeating the same procedure many times, you will wrap and repeat the "space". Sounds like Sci-Fi? Let's find out what it really means.
---
10/README (10/README.md)
Generative designs
It is not a surprise that after so much repetition and order the author is forced to bring some chaos.
Random
Randomness is a maximal expression of entropy. How can we generate randomness inside the seemingly predictable and rigid code environment?
Let's start by analyzing the following function:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Above we are extracting the fractional content of a sine wave. The
`sin()` values that fluctuate between `-1.0` and `1.0` have been chopped behind the floating point, returning all positive values between `0.0` and `1.0`. We can use this effect to get some pseudo-random values by "breaking" this sine wave into smaller pieces. How? By multiplying the resultant of `sin(x)` by larger numbers. Go ahead and click on the function above and start adding some zeros.By the time you get to
`100000.0` ( and the equation looks like this: `y = fract(sin(x)*100000.0)` ) you aren't able to distinguish the sine wave any more. The granularity of the fractional part has corrupted the flow of the sine wave into pseudo-random chaos.Controlling chaos
Using random can be hard; it is both too chaotic and sometimes not random enough. Take a look at the following graph. To make it, we are using a
`rand()` function which is implemented exactly like we describe above.`sin()` wave crest at `-1.5707` and `1.5707`. I bet you now understand why - it's where the maximum and minimum of the sine wave happens.If look closely at the random distribution, you will note that the there is some concentration around the middle compared to the edges.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
A while ago Pixelero published an interesting article about random distribution. I've added some of the functions he uses in the previous graph for you to play with and see how the distribution can be changed. Uncomment the functions and see what happens.
If you read Pixelero's article, it is important to keep in mind that our
`rand()` function is a deterministic random, also known as pseudo-random. Which means for example `rand(1.)` is always going to return the same value. Pixelero makes reference to the ActionScript function `Math.random()` which is non-deterministic; every call will return a different value.2D Random
Now that we have a better understanding of randomness, it's time to apply it in two dimensions, to both the
`x` and `y` axis. For that we need a way to transform a two dimensional vector into a one dimensional floating point value. There are different ways to do this, but the `dot()` function is particulary helpful in this case. It returns a single float value between `0.0` and `1.0` depending on the alignment of two vectors.<div class="codeAndCanvas" data="2d-random.frag"></div>
Take a look at lines 13 to 15 and notice how we are comparing the
`vec2 st` with another two dimensional vector ( `vec2(12.9898,78.233)`).* Try changing the values on lines 14 and 15. See how the random pattern changes and think about what we can learn from this.
* Hook this random function to the mouse interaction (
`u_mouse`) and time (`u_time`) to understand better how it works.Using the chaos
Random in two dimensions looks a lot like TV noise, right? It's a hard raw material to use to compose images. Let's learn how to make use of it.
`floor()` function we will generate an integer table of cells. Take a look at the following code, especially lines 22 and 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
After scaling the space by 10 (on line 21), we separate the integers of the coordinates from the fractional part. We are familiar with this last operation because we have been using it to subdivide a space into smaller cells that go from
`0.0` to `1.0`. By obtaining the integer of the coordinate we isolate a common value for a region of pixels, which will look like a single cell. Then we can use that common integer to obtain a random value for that area. Because our random function is deterministic, the random value returned will be constant for all the pixels in that cell.Uncomment line 29 to see that we preserve the floating part of the coordinate, so we can still use that as a coordinate system to draw things inside each cell.
Combining these two values - the integer part and the fractional part of the coordinate - will allow you to mix variation and order.
Take a look at this GLSL port of the famous
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10` maze generator.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Here I'm using the random values of the cells to draw a line in one direction or the other using the
`truchetPattern()` function from the previous chapter (lines 41 to 47).You can get another interesting pattern by uncommenting the block of lines between 50 to 53, or animate the pattern by uncommenting lines 35 and 36.
Master Random
Ryoji Ikeda, Japanese electronic composer and visual artist, has mastered the use of random; it is hard not to be touched and mesmerized by his work. His use of randomness in audio and visual mediums is forged in such a way that it is not annoying chaos but a mirror of the complexity of our technological culture.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Take a look at Ikeda's work and try the following exercises:
* Make rows of moving cells (in opposite directions) with random values. Only display the cells with brighter values. Make the velocity of the rows fluctuate over time.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Similarly make several rows but each one with a different speed and direction. Hook the position of the mouse to the threshold of which cells to show.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Create other interesting effects.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Using random aesthetically can be problematic, especially if you want to make natural-looking simulations. Random is simply too chaotic and very few things look
`random()` in real life. If you look at a rain pattern or a stock chart, which are both quite random, they are nothing like the random pattern we made at the begining of this chapter. The reason? Well, random values have no correlation between them what so ever, but most natural patterns have some memory of the previous state.In the next chapter we will learn about noise, the smooth and natural looking way of creating computational chaos.
#### For your toolbox
* LYGIA's generative functions are a set of reusable functions to generate patterns in GLSL. It's a great resource to learn how to use randomness and noise to create generative art. It's very granular library, designed for reusability, performance and flexibility. And it can be easily be added to any projects and frameworks.
---
10/Notes (10/notes.md)
Nicolas
L38 It returns a single float value between
`0.0` and `1.0` depending on the alignment of two vectors.* in fact it is the Cosine of the angle formed by 2 vectors multiplied by the length of each vectors : cos( a ) length( v0 ) length( v1 )
if we use normalised vectors, the cos() is multiplied by 1 1 and returns a number between -1 and 1*.
also, if the result is positive, both vectors point in the same direction, if zero, they're perpendicualer, if negative, they point in opposite directions.
* L62 famouse typo en
---
10/README Ch (10/README-ch.md)
生成设计
在经历了众多的重复与秩序之后,笔者自然而然地开始着手创造一些混沌。
随机
随机性是熵的最大表现。我们如何在看似可预测而且严苛的代码环境中生成随机性呢?
让我们从分析下面的函数着手:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
上面的例子中,我们提取了sin函数其波形的分数部分。值域为
`-1.0` 到 `1.0` 之间的`sin()` 函数被取了小数点后的部分(这里实际是指模1)),返回`0.0` 到 `1.0` 间的正值。我们可以用这种效果通过把正弦函数打散成小片段来得到一些伪随机数。如何实现呢?通过在`sin(x)`的值上乘以大些的数。点击上面的函数,在1后面加些0。当你加到
`100000.0` (方程看起来是这样的:`y = fract(sin(x)*100000.0)` ),你再也区分不出sin波了。小数部分的粒度将sine的循环变成了伪随机的混沌。控制混沌
使用随机会很难;它不是太混沌难测就是有时又不够混乱。看看下面的图例。要实现这样的效果,我们像之前描述的那样应用用
`rand()` 函数。`sin()` 在 `-1.5707` 和 `1.5707` 上有较大波动。我打赌你现在一定理解这是为什么——那就是sin取得最大值和最小值的地方。如果你仔细观察随机分布,你会注意到相比边缘,中部更集中。
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
不久前 Pixelero 发表了一篇关于随机分布的有意思的文章。 我添加了些前几张图所有的函数来供你试验,看看如何改变分布。取消函数的注释,看看发生什么变化。
如果你读下 Pixelero 的文章,一定谨记我们用的
`rand()` 是确定性随机,也被称作是伪随机。这就意味着, 就 `rand(1.)` 为例,总会返回相同的值。Pixelero 用 ActionSript 函数做了些参考,`Math.random()`,一个非确定性随机;每次调用都返回不同的值。2D 随机
现在我们对随机有了深入的理解,是时候将它应用到二维,
`x` 轴和 `y` 轴。为此我们需要将一个二维向量转化为一维浮点数。这里有几种不同的方法来实现,但 `dot()` 函数在这个例子中尤其有用。它根据两个向量的方向返回一个 `0.0` 到 `1.0` 之间的值。<div class="codeAndCanvas" data="2d-random.frag"></div>
看下第13行和15行,注意我们如何将
`vec2 st` 和另一个二维向量 ( `vec2(12.9898,78.233)`)。* 试着改变14和15行的值。看看随机图案的变化,想想从中我们能学到什么。
* 好好用鼠标(
`u_mouse`)和时间(`u_time`)调戏这个随机函数,更好的理解它如何工作。使用混沌
二维的随机看起来是不是像电视的噪点?对组成图像来说,随机是个难用的原始素材。让我们来学着如何来利用它。
`floor()` 函数,我们将会产生一个单元整数列表。看下下面的代码,尤其是22行和23行。<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
在缩放空间10倍后(在21行),我们将坐标系统的整数和小数部分分离。我们对最后一步操作不陌生,因为我们曾经用这种方法来将空间细分成
`0.0` 到 `1.0` 的小单元。我们根据得到坐标的整数部分作为一个通用值来隔离一个区域的像素,让它看起来像个单独的单元。然后我们可以用这个通用值来为这个区域得到一个随机值。因为我们的随机函数是伪随机,在那个单元内的所有像素返回的随机值都是一个常量。取消第29行保留我们坐标的小数部分,这样我们仍旧可以将其用作一个坐标系统,来在单元内部画图形。
结合这两个量 — 坐标的整数部分和小数部分 — 将使你可以结合变化和秩序。
看下这个著名的
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10`迷宫生成器的GLSL代码块。<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
这里我用前一章的
`truchetPattern()` 函数根据单元产生的随机值来随机画一个方向的对角线。你可以通过取消50到53行的代码块的注释得到其他有趣的图案,或者通过取消35和36行来得到图案的动画。
掌握随机
Ryoji Ikeda,日本电子作曲家、视觉艺术家,是运用随机的大师;他的作品是如此的富有感染力而难忘。他在音乐和视觉媒介中随机的运用,不再是混乱无序,反而以假乱真地折射出我们技术文化的复杂性。
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
看看 Ikeda 的作品并试试看下面的练习:
* 做按行随机移动的单元(以相反方向)。只显示亮一些的单元。让各行的速度随时间变化。
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* 同样地,让某几行以不同的速度和方向。用鼠标位置关联显示单元的阀值。
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* 创造其他有趣的效果。
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
完美地掌握随机之美是困难的,尤其是你想要让作品看起来很自然。随机仅仅是过于混乱了,真实生活中很少有东西看上去如此
`random()`。如果观察(玻璃床上)雨滴的肌理或是股票的曲线——这两个都挺随机的——但他们和本章开始的随机图案看起来不是同一对爹妈生的。原因?嗯,随机值之间没有什么相关性,而大多数自然图案(肌理)都对前一个状态有所记忆(基于前一个状态)。下一章我们将学习噪声,一种光滑 和 自然的 创作计算机混沌的方式。
---
10/README De (10/README-de.md)
Generative Designs
Nachdem wir in den vorangegangenen Kapiteln so viel über Wiederholungen und wohlgeformte Ordnungen gelernt haben, ist es nur konsequent, nun eine kleine Exkursion in das Reich des Chaotischen zu unternehmen.
Zufall
Zufälligkeit ist der konsequenteste Ausdruck von Entropie. Doch wie können wir Zufälle innerhalb einer so vorhersehbaren Umgebung wie die der Programmiersprache GLSL erzeugen?
Lass uns damit beginnen, die folgende Funktion zu analysieren:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Oben sehen wir das Ergebnis, wenn man nur den Nachkommateil der Werte einer Standardsinuskurve betrachtet. Die Funktionswerte von
`sin()` oszillieren zwischen `-1.0` und `1.0`. Hier wurde der Vorkommateil abgeschnitten, so dass wir es nur noch mit positiven Werten zwischen `0.0` und `0.99999999...` zu tun haben. Wir können diesen Effekt nutzen, um „pseudo-zufällige“ Zahlen zu generieren, indem wir die Sinuswelle in kleine Teilstücke „aufbrechen“. Wie das geht? Indem wir das Resultat von `sin(x)` mit großen Zahlen multiplizieren. Füge dem Faktor `1.0` in der obigen Formel einfach ein paar Nullen vor dem Komma hinzu – eine nach der anderen – und beobachte die Auswirkungen auf den Funktionsgraphen.Spätestens, wenn Du bei
`100000.0` angekommen bist (und die Formel so aussieht `y = fract(sin(x)*100000.0)` ), wirst Du die zugrundeliegende Sinuswelle nicht mehr erkennen können. Die Granularität des Nachkommaergebnisses hat den stetigen Fluss der Sinuswelle in ein pseudo-zufälliges Chaos verwandelt.Wir steuern das
Zufälligkeit für die Shader-Programmierung nutzbar zu machen, kann ganz schön kompliziert sein. Manchmal ist der Zufall zu chaotisch, dann wieder nicht zufällig genug. Schau Dir den folgenden Funktionsgraphen an. Wir haben ihn mit Hilfe der
`rand()`-Funktion konstruiert, die nach eben jenem Prinzip aufgebaut ist, das wir eben beschrieben haben.`sin()`-Funktion ihren Scheitel bei `-1.5707` und `1.5707` hat. Ich wette, Du ahnst bereits warum – weil dort das Maximum und Minimum einer Sinuswelle liegt, nämlich bei PI /2.0.Außerdem wirst Du im Hinblick auf die Zufallsverteilung feststellen, dass es eine gewisse Häufung im mittleren Bereich im Vergleich zu den äußeren Bereichen gibt.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Vor einiger Zeit hat der Autor mit dem Pseudonym Pixelero einen interessanten Artikel über Zufallsverteilungen veröffentlicht. Ich habe einige der Funktionen, die er dort anspricht, in dem obigen Graphen aufgeführt. Du kannst die Kommentare in den einzelnen Zeilen entfernen, um Dir diese Funktionen und ihre Zufallsverteilung anzuschauen.
Wenn Du den bereits zitierten Artikel von Pixelero liest, darfst Du nicht vergessen, das unsere
`rand()`-Funktionen deterministische Zufallswerte erzeugt, die als „pseudo-zufällig“ bezeichnet werden. Damit ist unter anderem gemeint, das beispielsweise `rand(1.)` immer den gleichen Wert zurückliefern wird. Pixelero geht in seinem Artikel auch auf die Funktion `Math.random()`unter ActionScript ein, die beispielsweise nicht-deterministisch ist. Jeder Aufruf mit einem gegebenen Wert als Funktionsargument liefert bei ihr andere Werte zurück.2D-Zufall
Mit diesem vertieften Verständnis von Zufall im Gepäck ist es an der Zeit, den Zufall in zwei Dimensionen wirken zu lassen – entlang der x-Achse und der y-Achse. Dafür müssen wir einen zweidimensionalen Vektor in einen eindimensionalen Fließkommawert verwandeln. Man kann dies auf verschiedenen Wegen erreichen, aber die Nutzung der
`dot()`-Funktion ist in diesem Fall besonders hilfreich. Sie liefert einen Fließkommawert zwischen `0.0` und `1.0` zurück, je nachdem wie die beiden Vektoren, deren Skalarprodukt sie bildet, zueinander stehen.<div class="codeAndCanvas" data="2d-random.frag"></div>
Bitte schaue Dir die Programmzeilen 13 bis 15 an und beobachte, wie wir den
`vec2 st` mit einem anderen (konstanten) Vektor (`vec2(12.9898,78.233)`) verknüpfen. Verändere die konstanten Werte in den Programmzeilen 14 und 15*. Beobachte, wie sich das Zufallsmuster dadurch ändert und überlege, was wir daraus lernen können.
* Verknüpfe die Zufallsfunktion mit der Mausposition (
`u_mouse`) und der Zeit (`u_time`), damit der Zufall noch weniger vorhersehbar wird.Das Chaos nutzen
Zufallswerte in zwei Dimensionen erinnern stark an das Rauschen im analogen TV, wenn das Antennensignal fehlt, nicht wahr? Als Ausgangsmaterial für die Erzeugung von Grafiken scheint es nur wenig geeignet. Aber lass uns einmal schauen, ob wir nicht doch etwas Sinnvolles damit anfangen können.
Unser erster Schritt besteht darin, ein Gitternetz zu unterlegen. Mit Hilfe der
`floor()`-Funktion werden wir eine Integer-Tabelle der Zellen erzeugen. Schau Dir den folgenden Programmcode an, insbesondere die Programmzeilen 22 und 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Nachdem wir den Koordinatenraum in ein Gitternetz mit 10 x 10 Feldern unterteilt haben (in Programmzeile 21), separieren wir den Vorkommateil der jeweiligen Koordinate von ihrem Nachkommateil. Wir kennen das bereits aus der Unterteilung des Koordinatenraums in kleinere Zellen, die sich jeweils von
`0.0` bis `1.0` erstrecken. Indem wir den ganzzahligen Teil der Koordinate isolieren, erhalten wir einen gemeinsamen Wert für alle Pixel innerhalb eines Feldes des Gitternetzes. Diesen Wert nutzen wir dann, um einen Zufallswert für diese Region zu generieren. Weil unsere Zufallsfunktion deterministisch ist, wird der Zufallswert für alle Bildpunkte innerhalb dieser Zelle gleich sein.Entferne die Kommentierung von Programmzeile 29, um zu beobachten, wie wir den Nachkommateil der Koordinate behalten. Dadurch können wir diesen Wert weiterhin nutzen, um innerhalb der Zellen zu zeichnen.
Indem wir diese beiden Werte kombinieren – den Vorkomma- und den Nachkommateil der Koordinate – können wir Ordnung und Chaos zusammenbringen.
Schau Dir einmal die GLSL-Portierung des berühmten Labyrinth-Generators in BASIC an, der dort nur aus einer einzigen Programmierzeile besteht:
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10text<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>Hier nutze ich die Zufallswerte der Zellen, um innerhalb der Zellen eine Linie mal in die eine, mal in die andere Richtung zu zeichnen. Ich setze dafür die
truchetPattern()`-Funktion aus dem vorangehenden Kapitel ein (es geht um die Programmzeilen 41 bis 47).Du erhältst ein sehenswertes animiertes Muster, wenn Du die Kommentarzeichen aus den Programmzeilen 35 und 36 entfernst.
Meister des Zufalls
Ryoji Ikeda, ein japanischer Komponist elektronischer Musik und Grafikkünstler, ist ein Meister des Zufälligen. Es fällt schwer, von seinen Arbeiten nicht berührt zu werden. Der Einsatz des Zufalls in seinen Werken ist so gestaltet, dass nicht verwirrendes Chaos, sondern ein Abbild der Komplexität unserer technischen Kultur entsteht.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Schau Dir die Arbeiten von Ikeda an und versuche Dich an den folgenden Aufgaben:
* Erzeuge Zeilen sich gegenläufig bewegender Zellen mit Farben aus Zufallswerten. Stelle nur die Zellen mit helleren Werten dar. Lasse sich die Geschwindigkeit der Bewegung im Laufe der Zeit verändern.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Erzeuge auf ganz ähnliche Weise mehrere Zeilen, von denen sich jede mit unterschiedlicher Geschwindigkeit und Richtung seitwärts bewegt. Verknüpfe die Mausposition mit dem Schwellenwert für die Anzeige der Zellen aufgrund ihrer Helligkeit.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Erzeuge weitere interessante Effekte, bei denen der Zufall eine Rolle spielt.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Zufallswerte auf ästhetische Art und Weise einzusetzen, kann schwierig sein, besonders wenn man Simulationen erstellen möchten, die die Natur nachahmen. Zufall ist einfach zu chaotisch und nur sehr wenige Dinge ums uns herum entstehen wirklich komplett zufällig. Wenn Du Dir beispielsweise das Muster von Regentropfen oder den Verlauf eines Aktienkurses anschaust – zwei Vorgänge, die Beide sicherlich eine Menge mit Zufall zu tun haben – wirst Du eben doch kein Chaos, sondern komplexe Strukturen und Abhängigkeiten erkennen. Schließlich sind hier jenseits des Zufälligen immer auch systemabhängige Korrelationen und ein Bezug auf vorherige Systemzustände am Werk.
Das nächste Kapitel beschäftigt sich deshalb mit Rauschen, dem sanften und der Natur nachempfunden Weg, um Dinge dynamisch und innerhalb des Chaotischen auch ein wenig strukturiert wirken zu lassen.
---
10/README Fr (10/README-fr.md)
Design génératif
Il n'est pas surprenant qu'après avoir passé autant de temps à organiser régulièrement les choses, l'auteur soit amené à introduire du chaos.
Aléatoire
L'aléatoire est la plus haute expression de l'entropie.
Comment pouvons nous créer de l'aléatoire dans un environnement apparemment aussi rigide et prévisible ?
Analysons la fonction suivante :
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
`fract()`) d'une fonction de sinus `sin()`.
Le sinus oscille entre `-1.0` et `1.0` et on récupère les chiffres après la virgule ce qui annule le signe (- ou +) et retourne une valeur absolue.
Nous pouvons utiliser ce procédé pour obtenir des valeurs pseudo-aléatoires en cassant cette oscillation en petits morceaux.
Comment ?
En multipliant le résultat de `sin(x)` par un grand chiffre.
Essayez d'ajouter quelques zéros au multiplicateur de `sin(x)` dans le code ci-dessus.En arrivant à
`100000.0` (l'équation ressemble à : `y = fract(sin(x)*100000.0)`), il n'est plus possible de distinguer l'oscillation de la sinusoïde.
La granularité de la partie fractionnelle a altéré les valeurs de la sinusoïde au point de la transformer en chaos pseudo-aléatoire.Contrôler le chaos
Utiliser l'aléatoire peut être difficile ; soit le résultat sera trop chaotique, soit il ne sera pas assez aléatoire.
Dans le graphe suivant, nous utilisons une fonction
`rand()`, implémentée exactement comme décrit ci-dessus.En regardant de plus près, on peut voir les pics que forme la sinusoïde à
`-1.5707` et `1.5707`.
Je parie que vous savez pourquoi - c'est là que la sinusoïde atteint ses valeurs minimum et maximum.En regardant la distribution des valeurs en Y, vous noterez également que les valeurs se concentrent autour de .5 plutôt que vers 0 et 1.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Il y a quelques temps, Pixelero a publié un article sur les distributions aléatoires.
J'ai ajouté quelques une des fonctions dont il se sert dans l'exemple ci-dessus pour que vous voyiez comment modifier une distribution.
Décommentez les lignes pour voir comment chacune se comporte.
Si vous avez lu l'article de Pixelero, il est important de garder à l'esprit que notre fonction
`rand()` est déterministe, aussi appelée pseudo-aléatoire.
Ce qui signifie par exemple que `rand(1.)` renverra toujours la même valeur alors que Pixelero se sert de la fonction
`Math.random()` qui est non-déterministe et renvoie une valeur différente à chaque appel.Aléatoire en 2D
Maintenant que nous en savons un peu plus sur l'aléatoire, nous pouvons nous atteler à la deuxième dimension ; un bruit en
`x` et `y`.
Nous aurons besoin pour ça de transformer un vecteur à deux dimensions en un chiffre aléatoire.
Il y a plusieurs façons de procéder mais la fonction `dot()` nous sera particulièrement utile dans ce cas précis.
Elle retourne un chiffre compris entre `-1.0` et `1.0` en fonction de l'alignement de deux vecteurs normalisés.Le _dot product_ ou produit scalaire renvoie le cosinus de l'angle formé par deux vecteurs normalisés.
Il permet donc de retrouver l'angle formé par les 2 vecteurs et de déterminer si les 2 vecteurs pointent dans la même direction.
<div class="codeAndCanvas" data="2d-random.frag"></div>
Aux lignes 13 à 15, nous utilisons le résultat du
`dot()` de `vec2 st` et d'un autre vecteur( `vec2(12.9898,78.233)`).* Essayez de changer les valeurs des lignes 14 et 15. Observez comment le motif aléatoire évolue et essayez d'en tirer des conclusions.
* Branchez cette fonction sur la position de la souris (
`u_mouse`) et le temps (`u_time`) pour mieux comprendre comment ça marche.Utiliser le chaos
L'aléatoire en 2D ressemble à la neige d'un téléviseur, n'est-ce pas ? C'est un métariau brut, difficile à utiliser tel quel pour construire une image, apprenons à mieux en tirer parti.
Notre première étape consistera à appliquer une grille ; en se servant de la méthode
`floor()`, nous allons créer un tableau de cellules.
Regardez bien le code suivant, notamment les lignes 22 et 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Après avoir multiplié l'échelle par 10 (ligne 21), nous séparons la partie entière de la partie fractionnelle du vecteur de coordonéées (st).
C'est une opération dont nous nous sommes servis au dernier chapitre, elle nous permet de subdiviser l'espace et d'obtenir des cellules dont les coordonnées sont normalisées entre
`0.0` et `1.0`.
La partie entière (le vecteur `vec2 floor(st)`) des coordonnées nous donne une valeur commune à chaque cellule, ce qui nous permet de trouver une valeur aléatoire pour chaque cellule plutôt que pour chaque fragment.
Du fait de la nature déterministe de notre fonction `rand()`, la valeur retournée par la fonction lorsqu'on lui passe plusieurs fois le même vecteur sera la même.
Comme tous les fragments d'une cellule passeront le même argument `ipos` à la fonction `rand()`, tous les fragments d'une cellule obtiendront la même couleur.Décommentez la ligne 29 pour voir que nous conservons également la partie fractionnelle des coordonnées. Nous pouvons toujours utiliser ces coordonnées pour dessiner quelque chose dans la cellule.
En combinant les deux valeurs - la partie entière et la partie fractionnelle des coordonnées - permet de créer des variations et de faire des mélanges de nos valeurs aléatoires.
Regardez cette implémentation du célèbre générateur de labyrinthes
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10`:<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Ici, je me sers de la valeur aléatoire d'une cellule pour dessiner une ligne dans une direction ou l'autre en appelant la méthode
`truchetPattern()` du chapitre précédent (lignes 41 à 47).Vous pouvez obtenir un motif intéressant en décommentant les lignes 50 à 53, ou l'animer en décommentant les lignes 35 et 36.
Maîtriser l'aléatoire
Ryoji Ikeda, un compositeur et artiste japonais maîtrise l'utilisation de l'aléatoire ; difficile de ne pas être touché par son oeuvre.
Dans ses oeuvres audio et vidéo, il utilise l'aléatoire de telle façon que ce n'est pas une série ennuyeuse de variations accidentelles mais plutôt un mirroir de la complexité de notre culture technologique.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Reagrdez le travail d'Ikeda et essayez les exercices suivants :
* Faites des rangées de cellules qui bougent dans des directions opposées, à des vitesses aléatoires. N'affichez que les cellules les plus lumineuses. Faites varier les vitesses dans le temps.
<a href="../edit.html#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Créez plusieurs lignes, chacune avec une direction et une vitesse différents, branchez la position de la souris comme seuil pour savoir quelles cellules afficher.
<a href="../edit.html#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Créez d'autres effets.
<a href="../edit.html#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Utiliser l'aléatoire de façon esthétique peut être difficile, en particullier si vous recherchez un rendu naturel.
L'aléatoire est beaucoup trop chaotique et dans la vraie vie, très peu de choses paraissent aussi aléatoires.
Si vous regardez la pluie ou le cours de la bourse, qui sont tous les deux aléatoires, vous verrez qu'ils n'ont rien en commun avec la méthode que nous avons créé en début de chapitre.
Pourquoi ? Les valeurs aléatoires ne sont pas corrélées entre elles, alors que les phénomènes naturels conservent souvent une trace de leur état précédent.
Au prochain chapitre, nous allons étudier le bruit, une manière plus organique de créer du chaos dans un shader.
---
10/README Id (10/README-id.md)
Desain generatif
Tidaklah mengherankan bahwa setelah begitu banyak pengulangan dan keteraturan, penulis terpaksa membuat kekacauan.
Acak
Keacakan adalah ekspresi entropi maksimal. Bagaimana kita bisa menghasilkan keacakan di dalam lingkungan kode yang tampaknya dapat diprediksi dan kaku?
Mari kita mulai dengan menganalisis fungsi berikut:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Di atas kami mengekstraksi konten pecahan dari gelombang sinus. Nilai
`sin()` yang berfluktuasi antara `-1.0` dan `1.0` telah dipotong di belakang floating point , mengembalikan semua nilai positif antara `0,0` dan `1,0`. Kita dapat menggunakan efek ini untuk mendapatkan beberapa nilai pseudo-random dengan "memecah" gelombang sinus ini menjadi potongan-potongan yang lebih kecil. Bagaimana? Dengan mengalikan resultan dari `sin(x)` dengan angka yang lebih besar. Silakan klik fungsi di atas dan mulai tambahkan beberapa angka nol.Pada saat Anda masuk ke
`100000.0` (dan persamaannya terlihat seperti ini: `y = fract(sin(x)*100000.0)`) Anda tidak dapat membedakan gelombang sinus lebih. Granularitas bagian pecahan telah merusak aliran gelombang sinus menjadi kekacauan acak semu.Mengontrol kekacauan
Menggunakan acak bisa jadi sulit; keduanya terlalu kacau dan terkadang tidak cukup acak. Perhatikan grafik berikut. Untuk membuatnya, kami menggunakan fungsi
`rand()` yang diimplementasikan persis seperti yang kami jelaskan di atas.`sin()` di `-1.5707` dan `1.5707` . Saya yakin Anda sekarang mengerti mengapa - di situlah maksimum dan minimum gelombang sinus terjadi.Jika melihat lebih dekat pada distribusi acak, Anda akan melihat bahwa terdapat konsentrasi di sekitar tengah dibandingkan dengan tepi.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Beberapa waktu yang lalu Pixelero menerbitkan artikel menarik tentang distribusi acak. Saya telah menambahkan beberapa fungsi yang dia gunakan pada grafik sebelumnya untuk Anda mainkan dan lihat bagaimana distribusinya dapat diubah. Hapus tanda komentar pada fungsinya dan lihat apa yang terjadi.
Jika Anda membaca artikel Pixelero, penting untuk diingat bahwa Fungsi
` rand()` adalah deterministik acak, juga dikenal sebagai pseudo-random. Yang artinya misalnya `rand (1.)` akan selalu mengembalikan nilai yang sama. Pixelero merujuk ke fungsi ActionScript `Math.random ()` yang non-deterministik; setiap panggilan akan mengembalikan nilai yang berbeda.2D Acak
Sekarang setelah kita memiliki pemahaman yang lebih baik tentang keacakan, saatnya untuk menerapkannya dalam dua dimensi, ke sumbu
`x` dan `y`. Untuk itu diperlukan suatu cara untuk mengubah vektor dua dimensi menjadi nilai floating point satu dimensi. Ada cara berbeda untuk melakukan ini, tetapi fungsi `dot() sangat membantu dalam kasus ini. Ini mengembalikan satu nilai float antara `0.0` dan `1.0` tergantung pada penyelarasan dua vektor.<div class="codeAndCanvas" data="2d-random.frag"></div>
Perhatikan baris 13 sampai 15 dan perhatikan bagaimana kita membandingkan
`vec2 st` dengan vektor dua dimensi lainnya (`vec2(12.9898,78.233)`).* Coba ubah nilai pada baris 14 dan 15. Lihat bagaimana pola acak berubah dan pikirkan tentang apa yang bisa kita pelajari dari ini.
* Kaitkan fungsi acak ini ke interaksi mouse (
`u_mouse`) dan waktu (`u_time`) untuk lebih memahami cara kerjanya.Menggunakan kekacauan
Acak dalam dua dimensi sangat mirip dengan noise TV, bukan? Ini adalah bahan mentah yang sulit digunakan untuk membuat gambar. Mari belajar bagaimana memanfaatkannya.
Langkah pertama kita adalah menerapkan grid padanya; menggunakan fungsi
`floor()` kita akan menghasilkan tabel integer sel. Perhatikan kode berikut, khususnya baris 22 dan 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Setelah menskalakan ruang sebesar 10 (pada baris 21), kami memisahkan bilangan bulat koordinat dari bagian pecahan. Kami terbiasa dengan operasi terakhir ini karena kami telah menggunakannya untuk membagi spasi menjadi sel-sel yang lebih kecil dari
`0.0` menjadi `1.0`. Dengan mendapatkan bilangan bulat dari koordinat kami mengisolasi nilai umum untuk wilayah piksel, yang akan terlihat seperti sel tunggal. Kemudian kita dapat menggunakan bilangan bulat umum tersebut untuk mendapatkan nilai acak untuk area tersebut. Karena fungsi acak kita bersifat deterministik, nilai acak yang dikembalikan akan konstan untuk semua piksel di sel itu.Hapus tanda komentar pada baris 29 untuk melihat bahwa kita mempertahankan bagian mengambang dari koordinat, jadi kita masih bisa menggunakannya sebagai sistem koordinat untuk menggambar sesuatu di dalam setiap sel.
Menggabungkan dua nilai ini - bagian bilangan bulat dan bagian pecahan dari koordinat - akan memungkinkan Anda untuk mencampur variasi dan urutan.
Lihatlah port GLSL dari
`10 PRINT CHR $ (205.5 + RND (1)) yang terkenal ini; : Generator labirin GOTO 10`.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Di sini saya menggunakan nilai acak sel untuk menggambar garis dalam satu arah atau yang lain menggunakan fungsi
`truchetPattern ()text/ Detailed source-code truncated for AI context efficiency. /
acak()` dalam kehidupan nyata. Jika Anda melihat pada pola hujan atau grafik saham, yang keduanya cukup acak, keduanya tidak seperti pola acak yang kita buat di awal bab ini. Alasannya? Nah, nilai acak tidak memiliki korelasi di antara mereka apa pun, tetapi sebagian besar pola alami memiliki ingatan tentang keadaan sebelumnya.Di bab berikutnya, kita akan belajar tentang noise, cara yang halus dan terlihat alami untuk menciptakan kekacauan komputasi.
---
10/README It (10/README-it.md)
Progettazione generativa
Non è una sorpresa che dopo aver passato tanto tempo a organizzare e a definire precisamente le cose, l'autore voglia introdurre un po' di caos.
Random
La casualità è la massima espressione d'entropia. Come possiamo generare casualità all'interno di un ambiente apparentemente prevedibile e rigido?
Iniziamo analizzando la seguente funzione:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Qui sopra abbiamo estratto il contenuto frazionario di una sinusoide. I valori di
`sin()` che oscillano tra `-1.0` e `1.0` sono stati tagliati dopo la virgola mobile, restituendo tutti i valori positivi tra `0.0` e `1.0`. Possiamo usare questo effetto per ottenere alcuni valori pseudo-casuali per "rompere" questa onda sinusoidale in pezzi più piccoli. Come? Moltiplicando la risultante di `sin(x)` con numeri più grandi. Provate ad aggiungere alcuni zeri alla funzione qui sopra.Arrivando a
`100000.0` ( l'equazione si presenta così: `y = fract(sin(x)*100000.0)` ) non si è più in grado di distinguere l'onda sinusoidale. La granularità della parte frazionaria ha corrotto il flusso della sinusoide al punto di trasformarla in caos pseudo-casuale.Controllare il caos
L'utilizzo della casualità può essere difficile; è sia troppo caotica e a volte non abbastanza casuale. Date un'occhiata al grafico seguente. Per farlo, stiamo utilizzando una funzione
`rand()` che viene implementata esattamente come si è descritto in precedenza.Dando uno sguardo più da vicino, si può vedere i picchi dell'onda di
`sin()` sono fra `-1.5707` e `1.5707`. Scommetto che ora sapete il perché: è dove la sinusoide raggiunge i suoi valori massimi e minimi.Se guardate la distribuzione del funzione random, si nota che vi è una certa concentrazione intorno a 0.5 rispetto che a 0.0 e 1.0.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Qualche tempo fa Pixelero ha pubblicato un interessante articolo sulla distribuzione random. Ho aggiunto per voi alcune delle funzioni che ha usato nel grafico precedente per vedere come la distribuzione può essere modificata. Decommentate le funzioni e guardate cosa succede.
Se andate a leggere l'articolo di Pixelero, è importante tenere a mente che la nostra funzione
`rand()` è deterministica casuale, in altre parole pseudo-casuale. Il che significa, per esempio, che `rand(1.)` restituisce sempre lo stesso valore. Pixelero fa riferimento alla funzione ActionScript `Math.random()` che è non-deterministica; cioè ogni chiamata restituirà un valore diverso.2D Random
Ora che abbiamo una migliore comprensione della casualità, è il momento d'applicarla alle dimensioni
`x` e `y`. Per fare ciò abbiamo bisogno di trasformare un vettore di due dimensioni in un float unidimensionale. Ci sono diversi modi per farlo, ma la funzione `dot()` è particolarmente utile in questo caso. Questa funzione restituisce un singolo valore decimale compreso tra `0.0` e `1.0` a seconda dell'allineamento dei due vettori.<div class="codeAndCanvas" data="2d-random.frag"></div>
Date un'occhiata al codice a partire della linea 13 fino alla 15 e noterete come stiamo confrontando il
`vec2 st` con un altro vettore a due dimensioni (`vec2(12.9898,78.233)`).* Provate a cambiare i valori delle linee 14 e 15. Vedrete come i pattern random cambiano e provate a trarne una conclusione.
* Collegate questa funzione random alla posizione del mouse (
`u_mouse`) e al tempo (`u_time`) per capire meglio il suo funzionamento.Usare il caos
Il random in due dimensioni assomiglia molto al rumore TV, giusto? Si tratta di un materiale grezzo difficile da usare se si vuole comporre delle immagini. Impariamo come usarlo.
Il primo passo è quello di applicargli una griglia; usando la funzione
`floor()` saremo in grado di generare una tabella di celle composta da integer. Date un'occhiata al seguente codice, in particolare alle linee 22 e 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Dopo il ridimensionamento dello spazio per 10 (alla linea 21), separiamo dalla parte frazionaria i numeri interi delle coordinate. Abbiamo una certa familiarità con questa ultima operazione, perché l'abbiamo utilizzata per suddividere lo spazio in celle più piccole che vanno da
`0.0` a `1.0`. Il valore intero della coordinata è un valore comune per una regione di pixel, che sarà simile a una singola cella. Quindi possiamo usare che il valore intero in comune per ottenere un valore random per quella zona. Poiché la nostra funzione random è deterministica, il valore restituito sarà costante per tutti i pixel in quella cella.Rimuovete il commento alla linea 29 per mantenere la parte float della coordinata, in modo da poterla usare come un sistema di coordinate per disegnare delle cose all'interno di ogni cellula.
Se combinate questi due valori - la parte intera e la parte frazionaria della coordinata - sarete in grado di mixare variabilità e ordine.
Date un'occhiata all'implementazione del famoso generatore di labirinti
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10` .<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Qui sto usando i valori random delle celle per disegnare una linea in una direzione o nell'altra utilizzando la funzione
`truchetPattern()` del capitolo precedente (linee da 41 a 47).È possibile ottenere un altro pattern interessante decommentando il blocco di righe tra la linea 50 a 53, o animare il pattern decommentando le linee 35 e 36.
Padroneggiare il Random
Ryoji Ikeda, compositore elettronico e artista visivo giapponese, è diventato un maestro nell'uso del random; è difficile non essere colpiti ed ipnotizzati dal suo lavoro. Nelle sue opere d'arte audio e visive è riuscito ad utilizzare la casualità in modo tale da non ottenere un fastidioso caos, ma uno specchio della complessità della nostra cultura tecnologica.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Date un'occhiata al lavoro di Ikeda e provate i seguenti esercizi:
* Create delle righe di celle in movimento (in direzioni opposte) con valori random. Mostrate solo le celle con valori più luminosi. Provate a rendere costante nel tempo la velocità delle righe.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Fate la stessa cosa con varie righe ma ogni volta con velocità e direzione diverse. Collegate la posizione del mouse alla soglia per decidere quale celle visualizzare.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Creare altri effetti interessanti.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
L'utilizzo del random può essere problematico dal punto di vista estetico, soprattutto se si vuole creare simulazioni che sembrano naturali. Il random è semplicemente troppo caotico e poche cose nella vita di tutti i giorni sembrano
`random()`. Se si considera un pattern generato dalla pioggia o un grafico azionario, che sono entrambi abbastanza casuali, questi non assomigliano per niente al pattern random che abbiamo fatto all'inizio di questo capitolo. La ragione? Beh, i valori random non hanno alcuna correlazione tra di loro e la maggior parte dei motivi naturali conserva una certa memoria dello stato precedente.Nel prossimo capitolo impareremo di più a proposito del rumore, una maniera semplice e dall'aspetto naturale di creare il caos computazionale.
---
10/README Kr (10/README-kr.md)
제너러티브 디자인
많은 반복과 질서가 있고 난 후에 작가는 약간의 혼란을 느낄 수 밖에 없다.
랜덤
랜덤은 엔트로피의 최대 표현이다. 어떻게 하면 예측 가능하고 엄격한 코드 환경 내에서 무작위성을 생성할 수 있을까?
먼저, 다음 함수를 분석해보자:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
위 함수는 사인파의 소수점이하를 추출하고 있다.
-1.0 에서 1.0 사잇값을 갖는 sin()의 소수점이하를 자르면 0.0 에서 1.0 사이의 양의 값이 반환된다. 이 현상을 이용하여 이 사인파를 더 작은 조각으로 "분할"함으로써 의사난수를 얻을 수 있다. 어떻게? sin(x)의 결과에 더 큰 숫자를 곱한다. 위에 있는 함수를 클릭하고 0을 추가해보아라.100000.0에 도달할 때 쯤이면(식은 다음과 같을 것:y = fract(sin(x)*100000.0)), 사인파를 더 이상 구분할 수 없게된다. 사인파의 흐름이 부분 부분 세분화로 인해 혼돈의 카오스가 되어버렸다.카오스 제어
랜덤을 사용하는것은 어려울 수 있다 - 지나치게 혼란스럽거나, 충분히 무작위적이지 않을 수 있기 때문이다. 다음 그래프를 보아라. 이를 위해 위에서 설명한 대로 정확히 구현된
rand()함수를 사용하고 있다.자세히 보면
-1.5707 및 1.5707에서 sin()파의 마루를 볼 수 있다.
이제 이해했을 것이다 - 그곳이 바로 사인파의 최대와 최소가 발생하는 곳 이다.랜덤 분포를 자세히 살펴보면, 가장자리에 비해 중간주위가 더 집중되어 있다는 것을 알 수 있다.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
얼마 전 Pixelero는 랜덤 분포에 대한 흥미로운 글을 출판했다. 이전 그래프에서 그가 사용하는 기능 중 일부를 추가해 여러분이 가지고 놀 수 있도록 했고, 분포가 어떻게 바뀔 수 있는지 알아보도록 했다. 함수의 주석을 해제해보며 어떻게 되는지 확인해보아라.
Pixelero's article를 읽어보면,
rand()함수는, 결정론적 난수(의사난수라고도 알려진)임을 강조한다. 즉, 예를 들어 rand(1.)는 항상 동일한 값을 반환한다. Pixelero는 모든 호출에 다른 값을 반환하는 비결정론적인 ActionScript 함수 Math.random()을 참고한다.2차원 랜덤
이제 무작위성을 더 잘 이해했으니
x축과 y축 모두에 2차원으로 적용해야 할 때이다. 그러기 위해서는 2차원 벡터를 1차원 부동 소수점 값으로 변환하는 방법이 필요하다. 다양한 방법이 존재하지만, 이 경우 dot() 함수가 특히 도움이 된다. 두 벡터의 정렬에 따라 0.0에서 1.0 사이의 단일 부동 소수점 값을 반환한다.<div class="codeAndCanvas" data="2d-random.frag"></div>
13~15번째 줄을 보면서
vec2 st를 또 다른 2차원 벡터(vec2(12.9898,78.233)와 어떻게 비교하고 있는지 알아보자.* 14행과 15행의 값을 변경해 보시오. 무작위 패턴이 어떻게 변하는지 보고 여기서 무엇을 배울 수 있는지 생각해 보시오.
* 이 랜덤 함수를 마우스 상호 작용(
u_mouse)과 시간(u_time)에 연결하여 작동 방식을 더 잘 이해할 수 있도록 해보자.카오스 이용
2차원 랜덤은 TV의 노이즈와 많이 닮았다. 그렇지 않은가?
이것은 이미지를 합성하는 데에는 사용하기 어려운 원료다. 이를 활용하는 방법을 배워보자.
floor()search=floor) 함수를 사용하여 셀의 정수 표를 생성할 것이다. 다음 코드, 특히 22번과 23번째 줄을 보시오.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
공간을 10배 확대한 후(21번째 줄에서), 좌표의 정수와 소수 부분을 분리한다. 우리는 이 마지막 작업에 익숙하다. 왜냐하면 공간을
0.0~1.0의 더 작은 셀로 세분화하기 위해서 이 방법을 사용해왔기 때문이다. 좌표의 정수를 구함으로써 단일 셀처럼 보이는 픽셀 영역에 대한 공통 값을 분리할 수 있다. 그런 다음 공통 정수를 사용하여 해당 영역에 대한 임의의 값을 얻을 수 있다. 랜덤 함수는 결정론적이기 때문에 반환되는 랜덤 값은 해당 셀의 모든 픽셀에 대해 일정하다.29번째 줄의 주석을 해제해보면 좌표의 소수 부분을 보존하는 것을 알 수 있고, 그 값을 각 셀 안에 있는 것들을 그리기 위한 좌표계로 사용할 수 있다.
이 두 값(정수 부분과 좌표의 소수 부분)을 조합해보면 다양성과 질서를 혼합할 수 있다.
다음은 유명한
10 PRINT CHR$(205.5+RND(1);: GOTO 10 미로 생성기의 GLSL 포팅 버전이다.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
앞 장(41~47번째 줄)의
truchetPattern() 함수를 사용하여 셀의 임의의 값을 사용하여 한 방향으로 선을 그린다.50~53행의 블럭을 주석 처리하여 또 다른 흥미로운 패턴을 얻을 수도 있고, 35와 36행에 주석을 해제함으로써 패턴을 애니메이션화할 수도 있다.
랜덤 마스터
일본의 전자 작곡가이자 시각 예술가인 Ryoji Ikeda는 랜덤 사용을 마스터했다. 그의 작품에 감동하고 매료되지 않는 것은 어렵다. 그가 시청각 매체에서 랜덤을 사용하는 것은 성가신 혼란이 아니라 우리 기술 문화의 복잡성의 거울이 되는 방식으로 만들어졌다.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Ikeda의 작업을 살펴보고 다음 연습에 도전하십시오:
* 랜덤 값으로 움직이는 셀(반대 방향)의 행을 만들어 보아라. 더 밝은 셀만 표시하여라. 행의 속도가 시간에 따라 변화하도록 하여라.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* 마찬가지로 여러 행을 만들지만, 각 행마다 속도와 방향을 다르게 만들어라. 표시할 셀의 임계값에 마우스 위치를 연동해라.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* 다른 흥미로운 효과를 만들어 보아라.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
심미적으로 랜덤을 사용하는것(특히, 자연스러워 보이는 시뮬레이션을 만들고 싶어서)은 문제가 될 수 있다. 랜덤은 단순히 지나치게 혼란스럽고 현실에서
random()처럼 보이는 것은 거의 없다. 빗물 패턴이나 재고 차트를 보면, 두 가지 모두 꽤 랜덤처럼 보이지만, 이 장 첫머리에 우리가 만든 랜덤 패턴과는 전혀 다르다. 이유는? 랜덤 값은 서로 상관관계가 없지만 대부분의 자연 패턴은 이전 상태에 대한 기억을 가지고 있다.다음 챕터에서는 컴퓨테이셔널 카오스를 일으키는 부드럽고 자연스러워 보이는 방법인 노이즈에 대해 알아볼 것 이다.
---
10/README Pl (10/README-pl.md)
Design generatywny
Nie jest zaskoczeniem, że po tylu zagadnieniach ładu i porządku autor zmuszony jest wprowadzić trochę chaosu.
Losowość
Losowość jest maksymalnym wyrazem entropii. Jak możemy wygenerować losowość wewnątrz pozornie przewidywalnego i sztywnego środowiska kodu?
Zacznijmy od analizy następującej funkcji:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
sin(), które oscylują pomiędzy -1.0 a 1.0 zostały posiekane, i sprowadzone do zakresu pomiędzy 0.0 a 1.0. Możemy wykorzystać ten efekt do uzyskania pseudolosowych wartości. W jaki sposób? Mnożąc wypadkową sin(x) przez większe liczby. Śmiało, zmodyfikuj powyższą funkcję, dodając zera do 1.0.Do czasu, gdy dojdziesz do
100000.0 (i równanie będzie wyglądało tak: y = fract(sin(x)*100000.0) ) nie jesteś już w stanie dostrzec sinusoidę. Ziarnistość części ułamkowej zepsuła falę sinusoidy w pseudolosowy chaos.Kontrolowanie chaosu
Używanie losowości może być trudne - czasami jest ona zbyt chaotyczna, a czasami niewystarczająco losowa. Przyjrzyj się poniższemu wykresowi. Aby go stworzyć, używamy funkcji
rand(), która jest zaimplementowana dokładnie tak, jak opisaliśmy powyżej.Przyglądając się bliżej, możesz dostrzec osobiliwości przy
-1.5707 i 1.5707. Założę się, że teraz rozumiesz dlaczego - to właśnie tam występuje maksimum i minimum fali sinusoidalnej.Jeśli przyjrzysz się bliżej rozkładowi losowemu, zauważysz, że istnieje pewne skupienie wokół środka w porównaniu do brzegów.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Jakiś czas temu Pixelero opublikował ciekawy artykuł o rozkładzie losowym. Dodałem kilka zakomentowanych funkcji, których używa, abyś mógł zobaczyć, jak można ten rozkład zmienić. Odkomentuj te funkcje i zobacz, co się stanie.
Czytając artykuł Pixelero, ważne jest, aby pamiętać, że nasza funkcja
rand() jest deterministyczna, pseudolosowa. Co oznacza, że na przykład rand(1.) zawsze zwróci tę samą wartość. Pixelero odwołuje się do funkcji ActionScript Math.random(), która jest niedeterministyczna - każde jej wywołanie zwróci inną wartość.Losowość 2D
Teraz, gdy mamy już lepsze zrozumienie losowości, czas zastosować ją w dwóch wymiarach, zarówno na osi
x jak i y. W tym celu potrzebujemy sposobu na przekształcenie dwuwymiarowego wektora w jednowymiarową wartość zmiennoprzecinkową. Można to zrobić na różne sposoby, ale szczególnie pomocna w tym przypadku jest funkcja dot(). Zwraca ona pojedynczą wartość zmiennoprzecinkową pomiędzy 0.0 a 1.0 w zależności od wzajemnej orientacji dwóch wektorów.<div class="codeAndCanvas" data="2d-random.frag"></div>
Przyjrzyj się liniom od 13 do 15 i zauważ, jak porównujemy
vec2 st z innym dwuwymiarowym wektorem ( vec2(12,9898,78,233)).* Spróbuj zmienić wartości w liniach 14 i 15. Zobacz, jak zmienia się wyświetlany losowy wzór i zastanów się, czego możemy się z tego nauczyć.
* Uzależnij tę funkcję losową od myszy (
u_mouse) i czasu (u_time), aby lepiej zrozumieć, jak działa.Wykorzystanie chaosu
Losowość w dwóch wymiarach wygląda bardzo podobnie do szumu telewizyjnego, prawda? To trudne do wykorzystania narzędzie do komponowania obrazów. Nauczmy się, jak zrobić z niego użytek.
Naszym pierwszym krokiem jest stworzenie tablicy kafelków; używając funkcji
floor() wygenerujemy tablicę, w której każdemu kafelkowi przyporządkowany jest unikalny wektor liczb całkowitych. Przyjrzyj się poniższemu kodowi, szczególnie liniom 22 i 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Po przeskalowaniu przestrzeni przez 10 (w linii 21) oddzielamy część całkowitą współrzędnych od części ułamkowej. Operacja uzyskiwania części ułamkowej jest nam dobrze znana, ponieważ używaliśmy jej do dzielenia przestrzeni na mniejsze kafelki o wartościach od
0.0 do 1.0. Uzyskując część całkowitą współrzędnej wyodrębniamy wspólną wartość dla całego kafelka. Następnie możemy użyć tej wspólnej liczby całkowitej, aby uzyskać losową wartość dla tego kafelka. Ponieważ nasza funkcja losowa jest deterministyczna, zwrócona wartość losowa będzie stała dla wszystkich pikseli w tym kafelku.Odkomentuj linię 29, aby zobaczyć, że zachowujemy część ułamkową współrzędnej, więc możemy nadal używać jej jako układu współrzędnych do rysowania rzeczy wewnątrz każdego kafelka.
Połączenie tych dwóch wartości - części całkowitej i części ułamkowej współrzędnej - pozwoli ci wymieszać zmienność i porządek.
Spójrz na poniższy GLSL'owy port słynnego generatora labiryntów
10 PRINT CHR$(205,5+RND(1)); : GOTO 10.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Tutaj wykorzystuję losowe wartości kafelków do rysowania linii w jednym lub drugim kierunku, używając funkcji
truchetPattern() z poprzedniego rozdziału (linie 41 do 47).Możesz uzyskać inny ciekawy wzór, odkomentowując blok linii między 50 a 53, natomiast odkomentowując linie 35 i 36 dodasz animację.
Ujarzmij losowość
Ryoji Ikeda, japoński kompozytor elektroniczny i artysta wizualny, ujarzmił losowość - trudno nie być poruszonym i zahipnotyzowanym przez jego prace. Jego użycie losowości w mediach audio-wizualnych to nie irytujący chaos, ale lustro złożoności naszej technologicznej kultury.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Zapoznaj się z pracami Ikedy i spróbuj wykonać następujące ćwiczenia:
* Utwórz rzędy ruchomych komórek (w przeciwnych kierunkach) o losowych wartościach. Wyświetlaj tylko komórki z jaśniejszymi wartościami. Spraw, aby prędkość rzędów zmieniała się w czasie.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Podobnie jak poprzednio, stwórz kilka rzędów kafelków, ale każdy z nich z inną prędkością i kierunkiem. Uzależnij próg wyświetlania kafelków od położenia myszy.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Stwórz inne ciekawe efekty.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Używanie losowości w estetyczny sposób może być problematyczne, zwłaszcza jeśli chcesz zrobić naturalnie wyglądające symulacje. Losowość jest po prostu zbyt chaotyczna i bardzo niewiele rzeczy wygląda
random() w prawdziwym życiu. Jeśli spojrzysz na wzór deszczu lub wykres giełdowy, które są dość losowe, nie przypominają one w niczym losowego wzoru, który stworzyliśmy na początku tego rozdziału. Powód? Cóż, wartości losowe nie mają żadnej korelacji między sobą, podczas gdy większość naturalnych wzorów ma jakąś pamięć o poprzednim stanie.W następnym rozdziale poznamy szum (ang. "noise"), płynny i naturalnie wyglądający sposób tworzenia chaosu obliczeniowego.
---
10/README Pt (10/README-pt.md)
Designs Generativos
Não é uma surpresa que, depois de tanta repetição e ordem, o autor seja forçado a trazer algum caos.
Aleatoriedade
Aleatoriedade é uma expressão máxima da entropia. Como podemos gerar aleatoriedade dentro de um ambiente de código tão rídigo e, aparentemente, previsível?
Vamos começar, analisando a seguinte função:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Acima, estamos extraindo o conteúdo fracionário de uma onda senoide. Os valores de
`sin()` que flutuam entre `-1.0` e `1.0` foram cortados depois do ponto flutuante, retornando todos valores positivos entre `0.0` e `1.0`. Podemos usar esse efeito para obter algums valores pseudo-aleatórios, quebrando essa onda de seno em pedaços menores. Como? Multiplicando o resultante de `sin(x)` por números maiores. Vá em frente e clique na função acima e comece a adicionar alguns zeros.Quando você chegar lá pelo
`100000.0` ( e a equação parecer com isso: `y = fract(sin(x)*100000.0)` ) você não vai mais ser capaz de distinguir a onda do seno. A granularidade da parte fracionária corrompeu o fluxo da onda em um caos pseudo-aleatório.Controlando o caos
Usar o random pode ser difícil; é ao mesmo tempo caótico demais, e às vezes pouco aleatório. Dê uma olhada no gráfico a seguir. Para fazê-lo, estamos usando a função
`rand()` que é implementada exatamente como descrevemos acima.`sin()` em `-1.5707` e `1.5707`. Eu aposto que agora você entende porque - é onde acontecem o máximo e o mínimo da onda de seno.Se olhar bem de perto na distribuição aleatória, você vai notar que existe uma concentração em torno do meio, comparado com as bordas.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Há um certo tempo, o Pixelero publicou um artigo interessante sobre distribuição aleatória. Eu adicionei alguma das funções que ele usa no gráfico anterior, para você brincar com elas, e ver como a distribuição pode ser alterada. Descomente as funções e veja o que acontece.
Se você ser o artigo do Pixelero, é importante ter em mente que nossa função
`rand()` é um random determinístico, também conhecido como pseudo-random. Isso quer dizer que, por exemplo, `rand(1.)` vai sempre retornar o mesmo valor. Pixelero faz referência à função `Math.random()` do ActionScriptm que é não-determinística; toda chamada vai retornar um valor diferente.2D Random
Agora que temos um entendimento melhor da aleatoriedade, é hora de aplicar isso em duas dimensões, tanto para o eixo
`x` quanto o `y`. Para isso, precisamos de uma maneira de transformar um vetor bi-dimensional em um valor de ponto flutuante, de uma dimensão. Existem formas diferentes de se fazer isso, mas a função `dot()` é bem útil nesse caso. Ela retorna um valor float entre `0.0` e `1.0` dependendo do alinhamento dos vetores.<div class="codeAndCanvas" data="2d-random.frag"></div>
Veja as linhas 13 a 15 e note como estamos comparando o
`vec2 st` com outro vetor bi-dimensional ( `vec2(12.9898,78.233)`).* Tente mudar os valores nas linhas 14 e 15. Veja como o padrão do random muda e pense no que podemos aprender com isso.
* Junte essa função random à interação com o mouse (
`u_mouse`) e tempo (`u_time`) para entender melhor como ela funciona.Usando o chaos
A random em duas dimensões parece bastante com o ruído da TV, não é? É um material difícil para usar para compor imagens. Vamos aprender como fazer uso disso.
`floor()` vamos gerar uma tabela inteira de células. Dê uma olhada nesse código a seguir, em especial as linhas 22 e 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Depois de escalar o espaço por 10 (na linha 21), nós separamos os inteiros das coordernadas da parte fracionária. Estamos familiares com essa última operação porque já vínhamos usando para subdividir um espaço em células menores, que vão de
`0.0` a `1.0`. Obtendo a parte inteira da coordenada, isolamos um valor comum para uma região de pixels, que vai parecer com uma célula simples. Então, podemos usar esse inteiro comum para obter um valor aleatório para essa área. Como nossa random é determinística, o valor aleatório retornado vai ser constante para todos os pixels naquela célula.Descomente a linha 29 e veja que nós preservamos a parte de ponto flutuante da coordenada, de modo que podemos ainda usar o sistema de coordenadas para desenhar coisas dentro de cada célula.
Combinar esses dois valores - a parte inteira e a fracionária da coordenada - vai te permitir misturar variação e ordem.
Dê uma olhada nessa versão portada para GLSL do famoso gerador de labirintos
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10`.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Aqui, estou usando os valores aleatórios de células para desenhar uma linha em uma direção ou outra, usando a função
`truchetPattern()` do capítulo anterior (linhas 41 a 47).Você pode conseguir outro padrão interessante ao descomentar o bloco de linhas entre 50 e 53, ou animar o padrão ao descomentar as linhas 35 e 36.
Seja Master no Random
Ryoji Ikeda, um japonês que é compositor eletrônico e artista visual, fez uso de mestre do random; é difícil não ser tocado e hipnotizado por seu trabalho. Seu uso da aleatoriedade em mídia visua e áudio é trabalhada de forma que não é vira um caos irritante, mas um espelho da complexidade de nossa cultura tecnológica.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Veja o trabalho do Ikeda e tente os seguintes exercícios:
* Faça linhas de células se movendo (em direções opostas) com valores aleatórios. Exiba apenas as células com valores mais claros. Faça a velocidade das células mudar ao longo do tempo.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* De modo similar, faça várias linhas, mas cada uma com velocidade e direção diferentes. Ligue a posição do mouse ao threshold de cada célula a exibir.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Crie outros efeitos interessantes.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Usar uma estética aleatória pode ser problemática, especialmente se você quer fazer simulações que pareçam naturais. A random é simplesmente caótica demais, e bem poucas coisas parecem com
`random()` na vida real. Se você olhar um padrão de chuva, ou gráfico de ações, que são bem aleatórios, eles não são nada como o padrão aleatório que fizemos no começo desse capítulo. A razão? Bem, valores aleatórios não têm correlação entre eles de qualquer modo, mas a maioria dos padrões naturais têm alguma memória do estado anterior.No próximo capítulo vamos aprender sobre ruído, a forma suave e de aparência natural de criar caos computacional.
---
10/README Ru (10/README-ru.md)
Генеративный дизайн
Не удивительно, что после стольких повторений и порядка, автор вынужден привнести немного хаоса.
Беспорядок
Случайность есть сильнейшее проявление энтропии. Но как получить беспорядок в казалось бы предсказуемом и строгом программном окружении?
Давайте начнём со следующей функции:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Выше мы извлекаем дробную часть синусоиды. Таким образом, значения синуса, плавно изменяющиеся от
`-1.0` до `1.0`, урезается до положительного диапазона от `0.0` до `1.0`. Этот эффект можно использовать для получения псевдослучайных значений, разбивая синусоиду на меньшие кусочки. Как? Умножением значения синуса на большие числа. Попробуйте добавить нулей в функцию выше.Когда коэффициент достигнет
`100000.0` (то есть когда функция примет вид `y = fract(sin(x)*100000.0)`), волны синусоиды станут неразличимыми. Дискретность дробной части повредила плавное течение синусоидальной волны, превратив её в псевдослучайный хаос.Управление хаосом
Использование беспорядка может стать непростой задачей. Он одновременно бывает слишком хаотичным и не слишком случайным. Посмотрите на следующий график. В нём мы используем функцию rand(), реализованную в точности как показано выше.
Присмотревшись, можно увидеть гребень синусоидальной волны в точках
`-1.5707` и `1.5707`. Легко понять почему: именно в этих точках синус достигает максимума и минимума.Так же при детальном рассмотрении видно, что значения больше концентрируются в середине, чем на краях полосы.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Некоторое время назад Pixelero опубликовал интересную статью о вероятностных распределениях. Я добавил несколько функций из неё в график выше, чтобы вы могли поиграться с ними и посмотреть как меняется распределение значений. Раскомментируйте функции и посмотрите что произойдёт.
Читая статью Pixelero, важно помнить, что функция
`rand()` является детерминированной, или псевдослучайной. Это означает, что, к примеру, `rand(1.)` всегда возвращает одно и то же значение. Pixelero упоминает недетерминированную функцию `Math.random()` из ActionScript, которая каждый раз возвращает разные значения.Двумерный беспорядок
Теперь у нас есть лучшее понимание беспорядка, и мы можем применить его в двумерном пространстве, к осям
`x` и `y` одновременно. Для этого нам нужен способ преобразования двумерного вектора в одномерное значение с плавающей точкой. Можно придумать много способов сделать это, например использовать скалярное произведение (`dot()`). В коде ниже функция от скалярного произведения возвращает единственное число в диапазоне от `0.0` до `1.0` в зависимости от взаимного расположения векторов.<div class="codeAndCanvas" data="2d-random.frag"></div>
Обратите внимания на строки с 13 по 15, где
`vec2 st` сравнивается с другим двумерным вектором `vec2(12.9898,78.233)`.* Попробуйте изменить значения в строках 14 и 15. Понаблюдайте за изменениями изображения я и подумайте, какую информацию из этого можно извлечь.
* Для лучшего понимания свяжите функцию взятия случайных значений с событиями мыши (
`u_mouse`) и временем (`u_time`).Использование хаоса
Случайные значения в двумерном изображении выглядят как шум ненастроенного телевизора, не правда ли? Это слишком жёсткий и сырой материал для создания изображений. Давайте сделаем его более полезным.
В качестве первого шага применим к случайному шуму сетку. Используя функцию
`floor()`, можно сгенерировать таблицу с целочисленным количеством клеток. Посмотрите на следующий код, особенно на строки 22 и 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
После увеличения пространства в 10 раз (строка 21), мы отделяем целые части координат от дробных. Эта операция нам уже знакома по разбиению пространства на клетки, в каждой из которых координаты изменяются от
`0.0` до `1.0`. Извлекая целую часть координаты, мы получаем общее число для всех пикселей одной клетки. Далее мы можем использовать это целое число чтобы сгенерировать случайное число для всей клетки. Так как случайная функция детерминирована, это случайное число получится одинаковым для всех пикселей клетки.Раскомментируйте строку 29, где мы используем сохранённую дробную часть координат пикселя в качестве нормированных координат внутри клетки.
Комбинация целой и дробной части координат позволяет смешивать порядок и изменчивость.
Посмотрите на GLSL-версию известного генератора лабиринтов
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10`.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
В нём используется случайное значение клетки для рисования линии в том или ином направлении с помощью функции
`truchetPattern()` из предыдущей главы (строки с 41 по 47).Раскомментируя строки с 50 по 53, можно получить ещё один интересный узор, а убрав комментарии со строк 35 и 36, вы увидите анимацию.
Мастер Беспорядка
Японский электронный музыкант и художник Рёдзи Икеда преуспел в использовании беспорядка. Сложно противостоять очарованию и гипнотизму его работ. Беспорядок особым образом вплетён в его работы: там он не создаёт раздражающий хаос, а отражает сложность нашей технологической культуры.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Посмотрите на работу Икеды и попробуйте выполнить следующее:
* Сделайте горизонтальное движение клеток со случайными значениями в противоположных направлениях. Показывайте только наиболее светлые клетки. Изменяйте скорость движения клеток со временем.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Точно так же сделайте несколько строк, только каждую с разной скоростью и направлением. Сделайте так, чтобы множество показываемых клеток зависело от положения мыши.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Создайте ещё какие-нибудь эффекты.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Сделать беспорядок эстетически привлекательным непросто, особенно если вы хотите делать симуляции, которые выглядят естественно. Беспорядок слишком хаотичен, и очень немногие из реальных вещей выглядят действительно случайно. Такие хаотичные вещи, как капли дождя или график биржевых котировок, не выглядят похожими на случайный рисунок, который мы делали вначале главы. В чём причина? Ну, случайные значения никак не коррелируют друг с другом, в то время как реальные вещи обычно «помнят» о своих предыдущих состояниях.
В следующей главе мы изучим шум. Это способ создания хаоса с помощью компьютера, который выглядит плавно и естественно.
---
10/README Tr (10/README-tr.md)
Üretken tasarımlar
Bu kadar tekrarlama ve düzenden sonra yazarın biraz kaos getirmeye zorlanması sürpriz değil.
Rastgelelik (Random)
Rastgelelik, entropinin maksimal ifadesidir. Görünüşte öngörülebilir ve katı kod ortamında nasıl rastgelelik üretebiliriz?
Aşağıdaki fonksiyonu analiz ederek başlayalım:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
Yukarıda bir sinüs dalgasının kesir içeriğini çıkarıyoruz.
`-1.0` ile `1.0` arasında dalgalanan `sin()` değerleri kayan noktanın arkasında kesildi ve `0.0` ile `1.0` arasında tüm pozitif değerleri döndürüyor. Bu etkiyi, bu sinüs dalgasını daha küçük parçalara "kırarak" bazı sözde rastgele değerler elde etmek için kullanabiliriz. Nasıl mı? `sin(x)` sonucunu daha büyük sayılarla çarparak. Devam edin, yukarıdaki fonksiyona tıklayın ve sıfırlar eklemeye başlayın.`100000.0`'a ulaştığınızda (ve denklem şöyle görünüyorsa: `y = fract(sin(x)*100000.0)`) artık sinüs dalgasını ayırt edemezsiniz. Kesir kısmının ayrıntı düzeyi sinüs dalgasının akışını sözde rastgele kaosa dönüştürdü.Kaosu kontrol etme
Rastgele kullanmak zor olabilir; hem çok kaotiktir hem de bazen yeterince rastgele değildir. Aşağıdaki grafiğe bakın. Bunu yapmak için, yukarıda tanımladığımız gibi uygulanan bir
`rand()` fonksiyonu kullanıyoruz.`sin()` dalgasının `-1.5707` ve `1.5707`'de tepe yaptığını görebilirsiniz. Şimdi nedenini anladığınıza bahse girerim — sinüs dalgasının maksimum ve minimumunun oluştuğu yer burasıdır.Rastgele dağılıma yakından bakarsanız, kenarlara kıyasla ortada bir miktar yoğunlaşma olduğunu not edeceksiniz.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Bir süre önce Pixelero, rastgele dağılım hakkında ilginç bir makale yayınladı. Kullandığı fonksiyonlardan bazılarını önceki grafiğe ekledim, oynayıp dağılımın nasıl değişebileceğini görmeniz için. Fonksiyonların yorumunu kaldırın ve ne olduğunu görün.
Pixelero'nun makalesini okursanız,
`rand()` fonksiyonumuzun determinist bir rastgele, yani sözde rastgele olduğunu aklınızda tutmak önemlidir. Bu, örneğin `rand(1.)`'in her zaman aynı değeri döndüreceği anlamına gelir. Pixelero, determinist olmayan ActionScript fonksiyonu `Math.random()`'a referans verir; her çağrı farklı bir değer döndürür.2B Rastgelelik
Artık rastgeleliği daha iyi anladığımıza göre, bunu iki boyutta, hem
`x` hem de `y` eksenine uygulamanın zamanı geldi. Bunun için iki boyutlu bir vektörü tek boyutlu bir kayan nokta değerine dönüştürmenin bir yoluna ihtiyacımız var. Bunu yapmanın farklı yolları vardır, ancak `dot()` fonksiyonu bu durumda özellikle faydalıdır. İki vektörün hizalamasına bağlı olarak `0.0` ile `1.0` arasında tek bir float değer döndürür.<div class="codeAndCanvas" data="2d-random.frag"></div>
13-15 arası satırlara bakın ve
`vec2 st`'yi başka bir iki boyutlu vektörle (`vec2(12.9898,78.233)`) nasıl karşılaştırdığımıza dikkat edin.* 14 ve 15. satırlardaki değerleri değiştirmeyi deneyin. Rastgele desenin nasıl değiştiğini görün ve bundan ne öğrenebileceğimizi düşünün.
* Bu rastgele fonksiyonu fare etkileşimine (
`u_mouse`) ve zamana (`u_time`) bağlayarak nasıl çalıştığını daha iyi anlayın.Kaosu kullanma
İki boyuttaki rastgelelik TV gürültüsüne çok benziyor, değil mi? Görüntü oluşturmak için kullanılması zor bir ham malzeme. Nasıl kullanacağımızı öğrenelim.
`floor()` fonksiyonunu kullanarak bir tam sayı hücre tablosu oluşturacağız. Özellikle 22 ve 23. satırlara dikkat ederek aşağıdaki koda bakın.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Uzayı 10'la ölçekledikten sonra (21. satırda), koordinatların tam sayılarını kesir kısmından ayırıyoruz. Bu son işleme aşinayız çünkü
`0.0`'dan `1.0`'a giden daha küçük hücrelere bölmek için kullanıyorduk. Koordinatın tam sayı kısmını elde ederek, tek bir hücre gibi görünecek bir piksel bölgesi için ortak bir değer izole ederiz. Sonra o alan için rastgele bir değer elde etmek üzere ortak tam sayıyı kullanabiliriz. Rastgele fonksiyonumuz determinist olduğundan, döndürülen rastgele değer o hücredeki tüm pikseller için sabit olacaktır.Kayan kısmı sakladığımızı görmek için 29. satırın yorumunu kaldırın, böylece her hücrenin içinde şeyler çizmek için bunu hâlâ bir koordinat sistemi olarak kullanabiliriz.
Bu iki değeri birleştirmek — koordinatın tam sayı kısmı ve kesir kısmı — çeşitleme ve düzeni karıştırmanıza olanak tanır.
Ünlü
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10` labirent oluşturucusunun GLSL portuna bir bakın.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Burada önceki bölümdeki
`truchetPattern()` fonksiyonunu (41-47 arası satırlar) kullanarak hücrelerin rastgele değerlerini bir yönde veya diğerinde bir çizgi çizmek için kullanıyorum.50-53 arası satırların yorumunu kaldırarak başka bir ilginç desen elde edebilir, veya 35 ve 36. satırların yorumunu kaldırarak deseni canlandırabilirsiniz.
Rastgelelikte Ustalık
Japon elektronik besteci ve görsel sanatçı Ryoji Ikeda, rastgelelik kullanımında ustalaşmıştır; çalışmalarından etkilenmemek ve hipnotize olmamak zordur. Ses ve görsel ortamda rastgelelik kullanımı o şekilde işlenmiştir ki, rahatsız edici bir kaos değil, teknolojik kültürümüzün karmaşıklığının bir aynasıdır.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Ikeda'nın çalışmalarına bakın ve aşağıdaki alıştırmaları deneyin:
* Rastgele değerlerle hareket eden hücre satırları yapın (zıt yönlerde). Sadece daha parlak değerlere sahip hücreleri gösterin. Satırların hızının zaman içinde dalgalanmasını sağlayın.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Benzer şekilde birçok satır yapın ama her biri farklı hız ve yönde olsun. Hangi hücrelerin gösterileceğinin eşiğini farenin konumuna bağlayın.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Başka ilginç efektler oluşturun.
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Rastgeleliği estetik olarak kullanmak sorunlu olabilir, özellikle doğal görünümlü simülasyonlar yapmak istiyorsanız. Rastgelelik basitçe çok kaotiktir ve gerçek hayatta çok az şey
`random()` gibi görünür. Bir yağmur desenine veya borsa grafiğine bakarsanız, ikisi de oldukça rastgeledir, bunlar bu bölümün başında yaptığımız rastgele desene hiç benzemez. Neden? Rastgele değerlerin birbirleriyle hiçbir korelasyonu yoktur, ancak çoğu doğal desen önceki durumun bir hafızasına sahiptir.Bir sonraki bölümde, hesaplamalı kaos yaratmanın düzgün ve doğal görünümlü yolu olan gürültüyü (noise) öğreneceğiz.
#### Araç kutunuz için
* LYGIA'nın üretken fonksiyonları, GLSL'de desenler üretmek için yeniden kullanılabilir fonksiyonlar kümesidir. Üretken sanat yaratmak için rastgelelik ve gürültüyü nasıl kullanacağınızı öğrenmek için harika bir kaynaktır. Yeniden kullanılabilirlik, performans ve esneklik için tasarlanmış çok ayrıntılı bir kütüphanedir. Ve herhangi bir projeye ve framework'e kolayca eklenebilir.
---
10/README Ua (10/README-ua.md)
Генеративний дизайн
Не дивно, що після стількох повторень наших карате-рухів і впорядкованості, автор змушений внести трохи хаосу.
Випадковість
[](http://www.ryojiikeda.com/project/testpattern/#testpattern_live_set)
Випадковість є максимальним проявом ентропії. Як ми можемо створити випадковість у, здавалося б, передбачуваному та суворому програмному середовищі?
Почнемо з аналізу наступної функції:
<div class="simpleFunction" data="y = fract(sin(x) * 1.0);"></div>
`sin()` коливаються між `-1.0` до `1.0`. Вище ми витягуємо дробову частину цієї синусоїди, "обрізаючи" та підіймаючи діапазон її негативних значень до позитивних в межах від `0.0` до `1.0`. Цей ефект можна використати, щоб отримати деякі псевдовипадкові значення, "розбивши" синусоїду на менші частини. Як? За допомогою помноження результату `sin(x)` на більші числа. Поверніться до прикладу і почніть додавати нулі до цілої частини множника.Коли дійдете до
`100000.0` і рівняння виглядатиме як "`y = fract(sin(x) * 100000.0)`", то більше не зможете розрізнити синусоїду. Деталізація дробової частини спотворила плавний потік синусоїди, перетворивши її у псевдовипадковий хаос.Управління хаосом
Використання випадковості може бути важкою справою. Інколи вона буває занадто хаотичною, а часом недостатньо випадковою. Подивіться на наступний графік. В ньому ми використовуємо функцію
`rand()`, яка реалізована так само як ми описали вище.`sin()`-хвилі по x-вісі біля значень `-1.5707` та `1.5707`. Б'юся об заклад, тепер ви розумієте чому — саме там відбувається максимум і мінімум синусоїди.Якщо уважно розглянути цей розподіл псевдовипадкових значень, то ви також помітите, що навколо середини y-вісі спостерігається більша концентрація значень, аніж на її краях.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x) * rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x), 5.0);"></div>
Свого часу Pixelero опублікував цікаву статтю про випадковий розподіл. Я додав декілька його прикладів до попереднього графіка, щоб ви могли пограти з ними та побачити, як можна змінити розподіл значень. Розкоментуйте рядки та подивіться, що станеться.
Читаючи статтю Pixelero, важливо мати на увазі, що наша функція
`rand()` є детерміновано випадковою, інакше кажучи, псевдовипадковою. Це означає, що, наприклад, `rand(1.)` завжди повертатиме одне й те саме значення. Pixelero у своїй статті посилається на недетерміновану ActionScript-функцію `Math.random()`, кожен виклик якої повертає різні значення.Двомірна випадковість
Тепер, коли ми краще розуміємо випадковість, настав час застосувати її у двомірному просторі, до осей
`x` та `y`. Для цього нам потрібен спосіб перетворити двовимірний вектор в одновимірне значення з рухомою крапкою. Існують різні способи зробити це, але функція `dot()` особливо корисна в цьому випадку. Вона повертає єдине float-значення між `0.0` і `1.0` залежно від розташування двох векторів.<div class="codeAndCanvas" data="2d-random.frag"></div>
Подивіться на рядки з 13 по 15 і зверніть увагу на те, як ми порівнюємо
`vec2 st` з іншим двовимірним вектором (`vec2(12.9898, 78.233)`).* Спробуйте змінити значення в рядках 14 і 15. Подивіться як змінюється зображення та поміркуйте, чого ми можемо з цього навчитися.
* Застосуйте цю функцію з отримання випадкових значень до взаємодії зі змінною для курсора миші (
`u_mouse`) та часом (`u_time`), щоб краще зрозуміти, як вона працює.Використання хаосу
Випадкові значення у двовимірному зображенні дуже схожі на телевізійний шум, чи не так? Це жорсткий та занадто сирий матеріал для створення зображень. Навчімось же ним користуватися.
Першим кроком застосуємо до нього сітку. За допомогою функції
`floor()` ми згенеруємо таблицю з цілочисельною кількістю клітинок. Подивіться на наступний код, особливо на рядки 22 і 23:<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Після збільшення простору у 10 разів (рядок 21), ми відокремлюємо цілі числа координат від дробової частини (рядок 22). Ми знайомі з цією операцією, тому що вже використовували її для поділу простору на менші комірки, що йдуть від
`0.0` до `1.0`. Отримавши ціле значення координати, ми виокремлюємо загальне значення кількості пікселів, необхідних для побудови одної комірки. Далі ми можемо використовувати це ціле число, щоб отримати випадкове значення для цієї області. Оскільки наша випадкова функція є детермінованою, то випадкове значення, що повертається, буде постійним для всіх пікселів у цій клітинці.Розкомментуйте рядок 29, щоб побачити, що ми зберігаємо дробову частину координати (рядок 23), тому ми все ще можемо використовувати її як нормовану систему координат для кожної окремої клітинки.
Поєднання цілої та дробової частин координати дозволить змішувати варіацію та порядок.
Подивіться на наступну GLSL-версію відомого генератора лабіринтів
`10 PRINT CHR$(205.5 + RND(1)); : GOTO 10`.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Тут я використовую випадкові значення комірок для малювання лінії в одному чи іншому напрямку за допомогою функції
`truchetPattern()` із попереднього розділу (рядки 41-47).Розкоментувавши блок рядків з 50 по 53, ви можете отримати інший цікавий патерн, або анімувати його, розкоментувавши рядки 35 і 36.
Майстер випадковості
Ryoji Ikeda — японський електронний композитор і візуальний художник, який опанував використання випадковості. Важко залишитись байдужим та не стати загіпнозованим його роботами. При використанні випадковості у своїх аудіо- та візуальних середовищах він не перетинає межу дратівливого хаосу та відзеркалює складність нашої технологічної культури.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Перегляньте роботу Ikeda і спробуйте виконати такі вправи:
* Створіть ряди рухомих у протилежних напрямках комірок із випадковими значеннями. Показуйте лише клітинки найбільш яскравими значеннями. Зробіть так, щоб швидкість рядків з часом змінювалася:
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Так само зробіть кілька рядів, але кожен з різною швидкістю та напрямком. Зробіть, щоб кількість видимих клітинок залежали від положення вказівника миші:
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Створіть інші цікаві ефекти:
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Використовування випадковості для отримання естетичних результатів може бути проблематичним, особливо якщо ви хочете зробити симуляції, що виглядатимуть природно. Випадковість занадто хаотична й дуже мало речей виглядають
рандомно у реальному житті. Якщо поглянути на патерн дощу або біржову діаграму, які є досить випадковими, то вони зовсім не схожі на ту модель випадковості, яку ми створили на початку цього розділу. В чому причина? Ну що ж, просто ці випадкові значення не мають ніякої кореляції між собою, а більшість природних шаблонів мають певну "пам'ять" про свій попередній стан.У наступному розділі ми дізнаємося про шум, плавний і природний спосіб створення обчислювального хаосу.
---
10/README Vi (10/README-vi.md)
Các thiết kế ngẫu nhiên
Sẽ không quá bất ngờ nếu ta đưa thêm một chút phá cách vào thiết kế sau khi đã vẽ rất nhiều họa tiết lặp lại mang nặng tính sắp đặt.
Ngẫu nhiên
Ngẫu nhiên là kết quả của của entropy, sau một quá trình vận động tự do. Làm thế nào để ta đưa được sự ngẫu nhiên đó vào trong các khung hình dễ đoán vẫn vẽ trước giờ ?
Hãy cùng phân tích đoạn code sau:
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
`sin()` cho kết quả nằm trong khoảng [-1.0, 1.0], đã trở thành nhiều khoảng gián đoạn có giá trị trong phạm vi [0.0, 1.0]. Đồ thị này khá là khó để dự đoán với nhiều người, vì thế ta có thể tận dụng để làm giả sự ngẫu nhiên, đặc biệt là khi ta giãn đồ thị trên theo trục tung bằng cách nhân kết quả từ hàm `sin(x)` với một số lớn. Hãy thử sửa code phía trên để chứng kiến tận mắt nhé.Khi bạn tăng dần con số lên tới mức
`100000.0` (công thức sẽ nhìn như sau: `y = fract(sin(x)*100000.0)`), bạn sẽ không thể nhìn ra chu kỳ của sóng sin nữa. Chính sự gián đoạn của phần thập phân đã bẻ gãy đường cong liên tục của sóng sin và tạo nên sự hỗn loạn, nhìn như chúng được phân bổ một cách ngẫu nhiên vậy.Ngẫu nhiên có kiểm soát
Sử dụng cơ chế ngẫu nhiên không hề đơn giản; đôi khi nó không đủ ngẫu nhiên, có lúc lại quá hỗn loạn. Hãy nhìn đồ thị dưới đây, nó được vẽ bằng cách dùng hàm
`rand()`, vốn sử dụng chính công thức phía trên để tạo ra sự ngẫu nhiên.Nếu nhìn kỹ, bạn sẽ thấy có khá nhiều điểm hội tụ ở
`-1.5707` và `1.5707`. Tôi cá là bạn biết tại sao - đó chính là vị trí các đỉnh sóng sin.Nhìn kỹ hơn nữa, bạn sẽ thấy ở giữa tập trung nhiều hơn phía trên và phía dưới.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x)*rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x),5.);"></div>
Cách đây vài năm, Pixelero đã đăng một bài báo thú vị về phân bổ ngẫu nhiên. Tôi có đặt vài hàm mà ông mô tả trong bài báo vào đoạn code phía trên. Hãy thử dùng các hàm đó để xem phân bố ngẫu nhiên thay đổi như thế nào nhé.
Nếu bạn đọc bài báo của Pixelero, thì hãy nhớ là hàm
`rand()` không thật sự ngẫu nhiên, nó được gọi là ngẫu nhiên giả (pseudo-random). Điều đó có nghĩa là nếu ta gọi hàm `rand(1.)` thì nó sẽ luôn trả về cùng 1 giá trị duy nhất. Pixelero có nhắc tới hàm `Math.random()` của ActionScript, là hàm ngẫu nhiên không xác định, mỗi lần gọi hàm này sẽ cho một kết quả khác nhau.Ngẫu nhiên hai chiều
Ta đã hiểu thêm một chút về việc sinh số ngẫu nhiên từ một số khác, giờ hãy thử tạo ra các số ngẫu nhiên từ một cặp số xem sao. Để làm được việc đó, ta cần một cách mã hoá vector 2 chiều thành 1 số thực. Có rất nhiều cách để làm việc này, nhưng tôi thấy hàm
`dot()` rất tiện trong việc này. Kết quả của phép tích vô hướng sẽ nằm trong khoảng [0.0, 1.0], tuỳ vào góc giữa 2 vector.<div class="codeAndCanvas" data="2d-random.frag"></div>
Hãy xem các dòng 13 và 15, bạn sẽ thấy tôi kết hợp
`vec2 st` với một vector khác ( `vec2(12.9898,78.233)`).* Thử thay đổi các giá trị ở hai dòng 14 và 15 xem các điểm ngẫu nhiên thay đổi thế nào.
* Thử kết hợp hàm ngẫu nhiên này với vị trí con trỏ chuột (
`u_mouse`) và thời gian (`u_time`) để hiểu rõ hơn.Tận dụng sự ngẫu nhiên
Ngẫu nhiên 2 chiều trông khá giống màn hình TV bị nhiễu phải không ? Nó là thành phần quan trọng trong việc tạo ra các hình ảnh đấy. Cùng tìm hiểu xem làm như vậy bằng cách nào nhé.
`floor()` để tạo ra một lưới mà mỗi ô ta sẽ chỉ tô đúng 1 màu. Hãy xem đoạn code dưới đây, đặc biệt là dòng 22 và 23.<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Sau khi chia canvas thành lưới 10x10 (dòng 21), ta có thể coi mỗi ô có 1 tọa độ riêng nằm trong khoảng [0.0, 1.0]. Phần này thì quen thuộc quá rồi thì ta đã làm tương ở chương trước. Để tô cùng một màu cho toàn bộ các điểm ảnh trong mỗi ô đó, ta sẽ chỉ dùng phần nguyên của tọa độ thay vì dùng cả phần thập phân. Sử dụng phần nguyên đó làm tham số cho hàm ngẫu nhiên, ta sẽ được một số khác, chính là màu mà ta sẽ tô cho các điểm ảnh trong từng ô. Và chính vì hàm ngẫu nhiên của ta được tính bằng công thức chung, nên tất cả điểm ảnh trong một ô sẽ có màu giống nhau.
Uncomment dòng 29 để kiểm chứng, mỗi ô vẫn là một "không gian con" có đầy đủ thông tin của từng điểm ảnh.
Kết hợp phần nguyên và phần thập phân, ta sẽ tạo ra vô vàn phiên bản ngẫu nhiên có kiểm soát.
Hãy xem đoạn code sinh ra mê cung dưới đây, nó sử dụng công thức nổi tiếng
`10 PRINT CHR$(205.5+RND(1)); : GOTO 10`.<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Ở đây tôi dùng các số ngẫu nhiên để vẽ những đường chéo, bằng cách sử dụng lại hàm
`truchetPattern()`, có tác dụng vẽ các họa tiết Truchet từ chương trước (dòng từ 41 tới 47).Một họa tiết khác cũng thú vị không kém sẽ hiện ra khi bạn uncomment các dòng từ 50 tới 53, thậm chí họa tiết sẽ chuyển động khi bạn uncomment dòng 35 và 36.
Sử dụng thành thạo sự ngẫu nhiên
Ryoji Ikeda, một nhạc sỹ nhạc điện tử và là một nghệ sỹ hiệu ứng thị giác, đã thành thục kỹ năng sử dụng sự ngẫu nhiên; sẽ thật khó để không xúc động và bị mê hoặc khi xem các tác phẩm của ông. Cách mà ông đưa sự ngẫu nhiên vào trong các tác phẩm âm nhạc và thị giác đã được mài dũa để không đem lại cảm giác lộn xộn mà vẫn phản ánh được sự phức tạp trong quá trình phát triển của văn hoá công nghệ.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Hãy chiêm ngưỡng các tác phẩm của Ikeda và thử:
* Vẽ các mã vạch di chuyển ngược chiều nhau với vận tốc thay đổi theo thời gian.
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Tương tự như trên, hãy vẽ nhiều hàng hơn nữa, vận tốc đa dạng hơn nữa. Sử dụng tọa độ X của con trỏ chuột làm ngưỡng thập phân để ẩn hiện các ô tuỳ ý.
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Tạo bất kỳ hiệu ứng hay ho nào mà bạn muốn
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Làm thế nào để ứng dụng sự ngẫu nhiên vào trong mỹ thuật rất khó, đặc biệt khi bạn muốn mô phỏng các hình dáng và chuyển động tự nhiên. Bản thân sự ngẫu nhiên sẽ quá hỗn loạn và có rất ít sự vật tự nhiên trông giống như vậy. Nếu không tin bạn cứ thử quan sát các giọt mưa hoặc bảng giá cổ phiếu trên sàn chứng khoán mà xem, chúng cũng khá ngẫu nhiên đó, nhưng không hề giống như các họa tiết lộn xộn mà ta vẽ ở đầu chương này đâu. Tại sao à ? Vì các giá trị ngẫu nhiên mà ta dùng không có sự liên quan, còn trong tự nhiên thì trạng thái tại một thời điểm của sự vật chịu ảnh hưởng từ trạng thái tại thời điểm trước đó.
Trong chương kế tiếp ta sẽ học về nhiễu, một cách để tạo ra sự hỗn loạn có kiểm soát và trông tự nhiên hơn.
---
10/SUMMARY Pt (10/SUMMARY-pt.md)
A vida é chata, se tudo é previsível. Embora nada seja realmente aleatório em computadores, podemos criar pseudo-aleatoriedade que pareça totalmente imprevisível usando truques simples para criar padrões e comportamentos mais interessantes.
---
10/SUMMARY (10/SUMMARY.md)
Life is boring if everything was predictable. Though nothing is truly random in computers, we can create pseudo-randomness that looks totally unpredictable using simple tricks to create more interesting patterns and behaviors.
---
11/README (11/README.md)
Noise
It's time for a break! We've been playing with random functions that look like TV white noise, our head is still spinning thinking about shaders, and our eyes are tired. Time to go out for a walk!
We feel the air on our skin, the sun in our face. The world is such a vivid and rich place. Colors, textures, sounds. While we walk we can't avoid noticing the surface of the roads, rocks, trees and clouds.
The unpredictability of these textures could be called "random," but they don't look like the random we were playing with before. The “real world” is such a rich and complex place! How can we approximate this variety computationally?
This was the question Ken Perlin was trying to solve in the early 1980s when he was commissioned to generate more realistic textures for the movie "Tron." In response to that, he came up with an elegant Oscar winning noise algorithm. (No biggie.)
The following is not the classic Perlin noise algorithm, but it is a good starting point to understand how to generate noise.
<div class="simpleFunction" data="
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i); //rand() is described in the previous chapter
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
In these lines we are doing something similar to what we did in the previous chapter. We are subdividing a continuous floating number (
`x`) into its integer (`i`) and fractional (`f`) components. We use `floor()` to obtain `i` and `fract()` to obtain `f`. Then we apply `rand()` to the integer part of `x`, which gives a unique random value for each integer.After that you see two commented lines. The first one interpolates each random value linearly.
glsly = mix(rand(i), rand(i + 1.0), f);
Go ahead and uncomment this line to see how this looks. We use the
`fract()` value store in f to `mix()` the two random values.At this point in the book, we've learned that we can do better than a linear interpolation, right?
Now try uncommenting the following line, which uses a
`smoothstep()` interpolation instead of a linear one.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
After uncommenting it, notice how the transition between the peaks gets smooth. In some noise implementations you will find that programmers prefer to code their own cubic curves (like the following formula) instead of using the
`smoothstep()`.glslfloat u = f f (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation
This smooth randomness is a game changer for graphical engineers or artists - it provides the ability to generate images and geometries with an organic feeling. Perlin's Noise Algorithm has been implemented over and over in different languages and dimensions to make mesmerizing pieces for all sorts of creative uses.
Now it's your turn:
* Make your own
`float noise(float x)` function.* Use your noise function to animate a shape by moving it, rotating it or scaling it.
* Make an animated composition of several shapes 'dancing' together using noise.
* Construct "organic-looking" shapes using the noise function.
* Once you have your "creature," try to develop it further into a character by assigning it a particular movement.
2D Noise
Now that we know how to do noise in 1D, it's time to move on to 2D. In 2D, instead of interpolating between two points of a line (
`rand(x)` and `rand(x)+1.0`), we are going to interpolate between the four corners of the square area of a plane (`rand(st)`, `rand(st)+vec2(1.,0.)`, `rand(st)+vec2(0.,1.)` and `rand(st)+vec2(1.,1.)`).Similarly, if we want to obtain 3D noise we need to interpolate between the eight corners of a cube. This technique is all about interpolating random values, which is why it's called value noise.
Like the 1D example, this interpolation is not linear but cubic, which smoothly interpolates any points inside our square grid.
Take a look at the following noise function.
<div class="codeAndCanvas" data="2d-noise.frag"></div>
We start by scaling the space by 5 (line 45) in order to see the interpolation between the squares of the grid. Then inside the noise function we subdivide the space into cells. We store the integer position of the cell along with the fractional positions inside the cell. We use the integer position to calculate the four corners' coordinates and obtain a random value for each one (lines 23-26). Finally, in line 35 we interpolate between the 4 random values of the corners using the fractional positions we stored before.
Now it's your turn. Try the following exercises:
* Change the multiplier of line 45. Try to animate it.
* At what level of zoom does the noise start looking like random again?
* At what zoom level is the noise is imperceptible?
* Try to hook up this noise function to the mouse coordinates.
* What if we treat the gradient of the noise as a distance field? Make something interesting with it.
* Now that you've achieved some control over order and chaos, it's time to use that knowledge. Make a composition of rectangles, colors and noise that resembles some of the complexity of a Mark Rothko painting.
Using Noise in Generative Designs
Noise algorithms were originally designed to give a natural je ne sais quoi to digital textures. The 1D and 2D implementations we've seen so far were interpolations between random values, which is why they're called Value Noise, but there are more ways to obtain noise...
As you discovered in the previous exercises, value noise tends to look "blocky." To diminish this blocky effect, in 1985 Ken Perlin developed another implementation of the algorithm called Gradient Noise. Ken figured out how to interpolate random gradients instead of values. These gradients were the result of a 2D random function that returns directions (represented by a
`vec2`) instead of single values (`float`). Click on the following image to see the code and how it works.Take a minute to look at these two examples by Inigo Quilez and pay attention to the differences between value noise and gradient noise.
Like a painter who understands how the pigments of their paints work, the more we know about noise implementations the better we will be able to use them. For example, if we use a two dimensional noise implementation to rotate the space where straight lines are rendered, we can produce the following swirly effect that looks like wood. Again you can click on the image to see what the code looks like.
glslpos = rotate2d( noise(pos) ) * pos; // rotate the space
pattern = lines(pos,.5); // draw lines
Another way to get interesting patterns from noise is to treat it like a distance field and apply some of the tricks described in the Shapes chapter.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Black splatter
color -= smoothstep(.35,.4,noise(st*10.)); // Holes on splatter
A third way of using the noise function is to modulate a shape. This also requires some of the techniques we learned in the chapter about shapes.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
For you to practice:
* What other generative pattern can you make? What about granite? marble? magma? water? Find three pictures of textures you are interested in and implement them algorithmically using noise.
* Use noise to modulate a shape.
What about using noise for motion? Go back to the Matrix chapter. Use the translation example that moves the "+" around, and apply some random and noise* movements to it.
* Make a generative Jackson Pollock.
Improved Noise
An improvement by Perlin to his original non-simplex noise Simplex Noise, is the replacement of the cubic Hermite curve ( _f(x) = 3x^2-2x^3_ , which is identical to the
`smoothstep()` function) with a quintic interpolation curve ( _f(x) = 6x^5-15x^4+10x^3_ ). This makes both ends of the curve more "flat" so each border gracefully stitches with the next one. In other words, you get a more continuous transition between the cells. You can see this by uncommenting the second formula in the following graph example (or see the two equations side by side here).<div class="simpleFunction" data="
// Cubic Hermite Curve. Same as SmoothStep()
y = xx(3.0-2.0*x);
// Quintic interpolation curve
//y = xxx(x(x*6.-15.)+10.);
"></div>
Note how the ends of the curve change. You can read more about this in Ken's own words.
Simplex Noise
For Ken Perlin the success of his algorithm wasn't enough. He thought it could perform better. At Siggraph 2001 he presented the "simplex noise" in which he achieved the following improvements over the previous algorithm:
* An algorithm with lower computational complexity and fewer multiplications.
* A noise that scales to higher dimensions with less computational cost.
* A noise without directional artifacts.
* A noise with well-defined and continuous gradients that can be computed quite cheaply.
* An algorithm that is easy to implement in hardware.
I know what you are thinking... "Who is this man?" Yes, his work is fantastic! But seriously, how did he improve the algorithm? Well, we saw how for two dimensions he was interpolating 4 points (corners of a square); so we can correctly guess that for three (see an implementation here) and four dimensions we need to interpolate 8 and 16 points. Right? In other words for N dimensions you need to smoothly interpolate 2 to the N points (2^N). But Ken smartly noticed that although the obvious choice for a space-filling shape is a square, the simplest shape in 2D is the equilateral triangle. So he started by replacing the squared grid (we just learned how to use) for a simplex grid of equilateral triangles.
The simplex shape for N dimensions is a shape with N + 1 corners. In other words one fewer corner to compute in 2D, 4 fewer corners in 3D and 11 fewer corners in 4D! That's a huge improvement!
In two dimensions the interpolation happens similarly to regular noise, by interpolating the values of the corners of a section. But in this case, by using a simplex grid, we only need to interpolate the sum of 3 corners.
How is the simplex grid made? In another brilliant and elegant move, the simplex grid can be obtained by subdividing the cells of a regular 4 cornered grid into two isosceles triangles and then skewing it until each triangle is equilateral.
Then, as Stefan Gustavson describes in this paper: _"...by looking at the integer parts of the transformed coordinates (x,y) for the point we want to evaluate, we can quickly determine which cell of two simplices that contains the point. By also comparing the magnitudes of x and y, we can determine whether the point is in the upper or the lower simplex, and traverse the correct three corner points."_
In the following code you can uncomment line 44 to see how the grid is skewed, and then uncomment line 47 to see how a simplex grid can be constructed. Note how on line 22 we are subdividing the skewed square into two equilateral triangles just by detecting if
`x > y` ("lower" triangle) or `y > x` ("upper" triangle).<div class="codeAndCanvas" data="simplex-grid.frag"></div>
All these improvements result in an algorithmic masterpiece known as Simplex Noise. The following is a GLSL implementation of this algorithm made by Ian McEwan and Stefan Gustavson (and presented in this paper) which is overcomplicated for educational purposes, but you will be happy to click on it and see that it is less cryptic than you might expect, and the code is short and fast.
Well... enough technicalities, it's time for you to use this resource in your own expressive way:
* Contemplate how each noise implementation looks. Imagine them as a raw material, like a marble rock for a sculptor. What can you say about the "feeling" that each one has? Squinch your eyes to trigger your imagination, like when you want to find shapes in a cloud. What do you see? What are you reminded of? What do you imagine each noise implementation could be made into? Following your guts and try to make it happen in code.
* Make a shader that projects the illusion of flow. Like a lava lamp, ink drops, water, etc.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Use Simplex Noise to add some texture to a work you've already made.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
In this chapter we have introduced some control over the chaos. It was not an easy job! Becoming a noise-bender-master takes time and effort.
In the following chapters we will see some well known techniques to perfect your skills and get more out of your noise to design quality generative content with shaders. Until then enjoy some time outside contemplating nature and its intricate patterns. Your ability to observe needs equal (or probably more) dedication than your making skills. Go outside and enjoy the rest of the day!
<p style="text-align:center; font-style: italic;">"Talk to the tree, make friends with it." Bob Ross
</p>
#### For your toolbox
* LYGIA's generative functions are a set of reusable functions to generate patterns in GLSL. It's a great resource to learn how to use randomness and noise to create generative art. It's very granular library, designed for reusability, performance and flexibility. And it can be easily be added to any projects and frameworks.
---
11/Notes (11/notes.md)
nicolas
* L78 replace fract() with floor() to match the prvious definition---
11/README Ch (11/README-ch.md)
Noise 噪声
稍事休息,我们来转换一下思路。我们已经玩过了看起来像电视白噪音的 random 函数,尽管脑子里还在嗡嗡地转着 shader,但是已经眼花缭乱了。是时候出去走走了。
我们感知得到浮动在皮肤上的空气,晒在脸上的阳光。世界如此生动而丰富。颜色,质地,声音。当我们走在路上,不免会注意到路面、石头、树木和云朵的表面的样子。
这些纹理的不可预测性可以叫做“random"(随机),但是它们看起来不像是我们之前玩的 random。「真实世界」是如此的丰富而复杂!我们如何才能才能用计算机模拟这些多样的纹理呢?
这就是 Ken Perlin 想要解答的问题。在20世纪80年代早期,他被委任为电影 “Tron”(电子世界争霸战)制作现实中的纹理。为了解决这个问题,他想出了一个优雅的算法,且获得了奥斯卡奖(名副其实)。
下面这个并不是经典的 Perlin noise 算法,但是这是一个理解如何生成 noise 的好的出发点。
<div class="simpleFunction" data="
float i = floor(x); // 整数(i 代表 integer)
float f = fract(x); // 小数(f 代表 fraction)
y = rand(i); //rand() 在之前的章节提过
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
这和之前章节我们做的事情很像。我们把单精度浮点
`x` 分割成它的整数部分 `i` 和小数部分 `f` 。我们用 `floor()` 获取 `i`,用 `fract()` 获取 `f`。然后我们 `rand()` x 的整数部分,即根据这个整数生成一个随机值。在这之后有两个被注释掉的语句。第一句的作用是线性插值。
glsly = mix(rand(i), rand(i + 1.0), f);
试试取消这句的注释,看一下会变成什么样子。注意我们储存在
f 中的 `fract()` 值`mix()`了两个随机值。`smoothstep()`进行插值。glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
在取消注释后,注意顶点的变化如何变得顺滑了起来。在一些 noise 的应用中你会发现程序员喜欢用他们自己的三次多项式函数(比如下面的例子),而不是用
`smoothstep()`。glslfloat u = f f (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation
这种 smooth randomness(平滑后的随机值)是一个图形工程师或者说艺术家的制胜法宝——它能生成非常有机的图像或几何形态。Perlin 的 noise 算法被无数次用到各种语言和各种维度的设计中,制作出无数迷人的作品。
现在轮到你了:
* 写你自己的
`float noise(float x)` 函数。* 用你的 noise 函数为图形制作动效,可以移动,旋转或改变大小。
* 用 noise 让一群图形一起“跳舞”。
* 用 noise 制做有机的形态。
* 创作一个“生物”,给它添加更多独特的动作,使其成为独特的角色。
2D Noise
现在我们知道了如何在一维使用 noise,是时候进入二维世界了。在 2D 中,除了在一条线的两点(
`fract(x)` 和 `fract(x)+1.0`)中插值,我们将在一个平面上的方形的四角(`fract(st)`, `fract(st)+vec2(1.,0.)`, `fract(st)+vec2(0.,1.)` 和 `fract(st)+vec2(1.,1.)`)中插值。同样,如果我们想要在三维中使用 noise,就需要在一个立方体的8个角中插值。这个技术的重点在于插入随机值,所以我们叫它 value noise。
就像一维的那个例子,这个插值不是线性的,而是三次方的,它会平滑地在方形网格中插入点。
让我们来看一下这个方程。
<div class="codeAndCanvas" data="2d-noise.frag"></div>
我们先把空间大小变成五倍(第 45 行)以便看清栅格间的插值。然后在 noise 函数中我们把空间分成更小的单元。我们把它的整数部分和非整数部分都储存在这个单元里。我们计算整数位置的顶点的坐标,并给每个顶点生成一个随机值(第 23 - 26 行)。最后,在第 35 行用我们之前储存的小数位置的值,在四个顶点的随机值之间插值。
现在又到你了。试试下面的练习:
* 改变第 45 行的乘数。添加动态试试。
* 缩放到什么程度会让 noise 再度变得像 random?
* 缩放到什么程度就看不出来 noise 了?
* 试试看把 noise 函数和鼠标位置连起来。
* 如果我们把 noise 的斜率处理成距离场(distance field)会怎么样?用它来做点有趣的事情。
* 现在你已经可以在混沌和有序之间进行一些操控了,是时候把这些知识用起来了。用长方形,色彩,和 noise 做一幅 Mark Rothko 的复杂的画作吧。
生成式设计中的 noise 应用
Noise 算法的设计初衷是将难以言说的自然质感转化成数字图像。在目前我们看到的一维和二维的实践中,都是在random values(随机值)之间插值,所以它们才被叫做 Value Noise,但是还有很多很多获取 noise 的方法……
如你所见,value noise 看起来非常“块状”。为了消除这种块状的效果,在 1985 年 Ken Perlin 开发了另一种 noise 算法 Gradient Noise。Ken 解决了如何插入随机的 gradients(梯度、渐变)而不是一个固定值。这些梯度值来自于一个二维的随机函数,返回一个方向(
`vec2` 格式的向量),而不仅是一个值(`float`格式)。点击下面的图片查看代码,看这个函数是如何运作的。花一分钟来看看 Inigo Quilez 做的两个例子,注意 value noise 和 gradient noise的区别。
就像一个画家非常了解画上的颜料是如何晕染的,我们越了解 noise 是如何运作的,越能更好地使用 noise。比如,如果我们要用一个二维的 noise 来旋转空间中的直线,我们就可以制作下图的旋涡状效果,看起来就像木头表皮一样。同样地,你可以点击图片查看代码。
glslpos = rotate2d( noise(pos) ) * pos; // 旋转空间
pattern = lines(pos,.5); // 画直线
另一种用 noise 制作有趣的图案的方式是用 distance field(距离场)处理它,用用 第七章提到的招数。
glslcolor += smoothstep(.15,.2,noise(st*10.)); // 黑色的泼溅点
color -= smoothstep(.35,.4,noise(st*10.)); // 泼溅点上的洞
第三种方法是用 noise 函数来变换一个形状。这个也需要我们在第七章学到的技术。
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
给你的练习:
* 你还能做出什么其他图案呢?花岗岩?大理石?岩浆?水?找三种你感兴趣的材质,用 noise 加一些算法把它们做出来。
* 用 noise 给一个形状变形。
* 把 noise 加到动作中会如何?回顾第八章。用移动 “+” 四处跑的那个例子,加一些 random 和 noise 进去。
* 用代码生成波洛克(Jackson Pollock)的画。
Simplex Noise
对于 Ken Perlin 来说他的算法所取得的成功是远远不够的。他觉得可以更好。在 2001 年的 Siggraph(译者注:Siggraph是由美国计算机协会「计算机图形专业组」组织的计算机图形学顶级年度会议)上,他展示了 “simplex noise”,simplex noise 比之前的算法有如下优化:
* 它有着更低的计算复杂度和更少乘法计算。
* 它可以用更少的计算量达到更高的维度。
* 制造出的 noise 没有明显的人工痕迹。
* 有着定义得很精巧的连续的 gradients(梯度),可以大大降低计算成本。
* 特别易于硬件实现。
我知道你一定在想:“这人是谁?”是的,他的工作非常杰出!但是说真的,他是如何优化算法的呢?我们已经知道在二维中他是如何在四个点(正方形的四个角)之间插值的;所以没错你已经猜到了,对于三维(这里有个示例)和四维我们需要插入 8 个和 16 个点。对吧?也就是说对于 N 维你需要插入 2 的 n 次方个点(2^N)。但是 Ken 很聪明地意识到尽管很显然填充屏幕的形状应该是方形,在二维中最简单的形状却是等边三角形。所以他把正方形网格(我们才刚学了怎么用)替换成了单纯形等边三角形的网格。
这时 N 维的形状就只需要 N + 1 个点了。也就是说在二维中少了 1 个点,三维中少了 4 个,四维中则少了 11 个!巨大的提升!
在二维中插值过程和常规的 noise 差不多,通过在一组点之间插值。但是在这种情况下,改用单纯形网格,我们只需要给总共 3 个点插值。
这个单纯形网格是如何制作的?这是另一个聪明绝顶而十分优雅的做法。可以先把常规的四角网格分成两个等腰三角形,然后再把三角形歪斜成等边三角形。
然后,就像 Stefan Gustavson 在这篇文献中说的: “……通过观察转换后坐标的整数部分,我们就可以快速地判断哪个包含着两个单纯形的单元含有我们所需的点。并且通过比较 x 和 y 的大小,我们就可以判断这个点是在上三角还是下三角中,并且遍历这个正确的三角形。”
在下面的代码中你可以取消第 44 行的注释,看一看网格是如何歪斜的,然后取消第 47 行的注释,看一看如何建造单纯形网格。注意第 22 行中我们仅仅通过判断 if
`x > y` (下三角) 还是 `y > x` (上三角),就把歪斜过的正方形切成了两个等腰三角形。<div class="codeAndCanvas" data="simplex-grid.frag"></div>
另一个 Simplex Noise 的优化是把三次 Hermite 函数(Cubic Hermite Curve:_f(x) = 3x^2-2x^3_,和
`smoothstep()` 一样)替换成了四次 Hermite 函数( _f(x) = 6x^5-15x^4+10x^3_ )。这就使得函数曲线两端更“平”,所以每个格的边缘更加优雅地与另一个衔接。也就是说格子的过渡更加连续。你可以取消下面例子的第二个公式的注释,亲眼看看其中的变化(或者看这个例子)。<div class="simpleFunction" data="
// 三次 Hermite 曲线。和 SmoothStep() 一样
y = xx(3.0-2.0*x);
// 四次 Hermite 曲线
//y = xxx(x(x*6.-15.)+10.);
"></div>
注意曲线的末端发生了怎样的变化。你可以阅读 Ken 自己的解释了解更多。
所有这些进展汇聚成了算法中的杰作 Simplex Noise。下面是这个算法在 GLSL 中的应用,作者是 Ian McEwan,以这篇论文发表,对于我们的教学而言太复杂了,但你可以点开看看,也许没有你想象得那么晦涩难懂。
好了,技术细节就说到这里,现在你可以利用它好好自由发挥一下:
* 凝神思考每个 noise 的实例的模样。设想它们是你的原材料,就像雕塑家手中的大理石。你觉得每一个带给你怎样不同的“感觉“?眯起你的眼睛释放想象力,就像你在观察云朵的形状的时候那样。你看到了什么?你想起了什么?你觉得每个 noise 生成的图像可以用来做成什么?放开胆量去做吧,用代码实现它。
* 做一个 shader 来表现流体的质感。比如像熔岩灯,墨水滴,水,等等。
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* 用 Simplex Noise 给你现在的作品添加更多的材质效果。
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
在本章我们介绍了一些操控混沌的方法。这并不是一件简单的工作!成为 noise 超级大师需要时间和努力。
在下面的章节我们会看到一些很有名的技术,来修葺你的技能树,并且从 noise 中学到更多,并利用 shader 设计出更多优质的生成式艺术作品。在那之前,去外面走走,享受深入思考自然和错综复杂的图案的时光吧。培养洞察力也许需要和动手能力相同(甚至更多)的努力。出门走走享受今天剩余的时光吧!
<p style="text-align:center; font-style: italic;">
“和树聊聊天吧,和它交个朋友。” Bob Ross
</p>
---
11/README De (11/README-de.md)
Rauschen
Im letzten Kapitel haben wir mit Zufallsfunktionen gearbeitet, die wie weißes Rauschen auf dem Fernsehbildschirm wirken. Nach so vielen Shader-Funktionen auf einmal dreht sich Dir bestimmt der Kopf und vielleicht sind auch Deine Augen etwas müde. Jetzt ist ein guter Moment, um einen kleinen Spaziergang zu unternehmen und ein wenig nach Luft zu schnappen.
Draußen im Freien spüren wir den Luftzug auf unserer Haut und die Sonne in unserem Gesicht. Die Erde ist ein so lebendiger und vielgestaltiger Ort voller Farben, Texturen und Klängen. Während wir uns in der Natur bewegen, können wir gar nicht anders, als die unterschiedlichen Oberflächen von Straßen, Gestein, Bäumen und Wolken wahrzunehmen.
Die Unvorhersehbarkeit dieser Texturen könnte man als „zufällig“ auffassen. Dabei wirken sie so gar nicht wie die Zufallsstrukturen, mit denen wir uns im letzten Kapitel befasst haben. Die Wirklichkeit ist ein so komplexer Ort! Wir kann es uns gelingen, ihr Antlitz mit Hilfe von Algorithmen nachzuahmen?
Diese Frage beschäftigte Ken Perlin in den frühen 1980er Jahren, als er den Auftrag erhielt, realistische Texturen für den Hollywood-Film „Tron“ zu erschaffen. Er entwickelte damals eine elegante Vorgehensweise, die als „Noise Algorithmus“ bekannt und sogar mit einem Oskar prämiert wurde (kein Witz).
Die folgende Funktion entspricht nicht dem klassischen Noise-Algorithmus von Perlin, aber sie ist ein guter Ausgangspunkt, um die Erzeugung „verrauschter“-Strukturen zu begreifen.
<div class="simpleFunction" data="
float i = floor(x); // ganzzahliger Teil
float f = fract(x); // Nachkommateil
y = rand(i); //rand() wurde im vorangegangenen Kapitel beschrieben
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
In diesen Zeilen gehen wir ähnlich vor, wie im vorangegangenen Kapitel. Wir teilen eine fortlaufende Fließkommazahl (
`x`) in ihren ganzzahligen Vorkommateil (`i`) und ihren Nachkommateil (`f`) auf. Wir nutzen dabei `floor()`, um `i` zu erhalten, sowie `fract()` für die Berechnung von `f`. Dann wenden wir `rand()` auf den ganzzahligen Teil von `x` an, wodurch wir für jede Ganzzahl eine einzigartige Zufallszahl erhalten.Dahinter folgen zwei Programmzeilen, die zunächst auskommentiert sind. Die Erste davon interpoliert zwischen dem Zufallswert (
`rand(i)`) und dem Zufallswert von (`rand(i+1)`) (also zwischen zwei ganz unterschiedlichen Zahlen) auf lineare Weise, wobei der Abstand für die Interpolation jeweils durch (`f`) bestimmt wird.glsly = mix(rand(i), rand(i + 1.0), f);
Fahre fort, indem Du die Kommentarzeichen für diese Zeile entfernst und Dir das Ergebnis anschaust.
An diesem Punkt unseres kleinen GLSL-Kurses wissen wir bereits, dass es noch etwas besseres als eine simple lineare Interpolation gibt, nicht wahr? Entferne deshalb die Kommentarzeichen aus der nächsten Zeile, die
`smoothstep()` nutzt, um bei der Interpolation weichere Verläufe und Übergänge zu erzielen.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Sobald Du die Kommentarzeichen aus dieser Zeile entfernt hast, erscheint ein anderes Ergebnis. In einigen Implementationen für Rausch-Funktionen verwenden die Autoren lieber ihre eigenen kubischen Kurven an Stelle von
`smoothstep()`, wie etwa im folgenden Beispiel.glslfloat u = f f (3.0 - 2.0 * f ); // kubische Verlaufskurve
y = mix(rand(i), rand(i + 1.0), u); // angewandt auf die Interpolation
Diese sanfte Zufälligkeit ist das entscheidende Merkmal, auf das es vielen Grafikkünstlern ankommt. Erst dadurch wird es möglich, geometrische Formen und Bilder zu erstellen, die so organisch wirken, als kämen sie direkt aus der Natur. Perlins Noise-Algorithmus wurde seit seiner Veröffentlichung immer wieder in ganz unterschiedlichen Programmiersprachen und für verschiedene Dimensionen implementiert. Er hat Grafiker und Grafikkünstler auf der ganzen Welt bei ihren Arbeiten unterstützt.
Und nun bist Du dran:
* Entwickle Deine eigene
`float noise(float x)`-Funktion.* Nutze Deine Noise-Funktion, um Formen zu animieren, indem Du sie bewegst, rotierst oder skalierst.
* Erstelle eine animierte Komposition aus mehreren Formen, die „miteinander“ tanzen, indem Du auf Noise-Funktionen zurückgreifst.
* Baue organisch wirkende Formen mit Hilfe der Noise-Funktion.
* Sobald Du auf diese Weise eine eigene „Kreatur“ erschaffen hast, versuche ihr Lebendigkeit einzuhauchen, indem Du sie auf ganz charakteristische Weise bewegst.
2D-Rauschen
Jetzt, wo wir wissen, wie man Rauschen in einer Dimension erzeugt, können wir uns dem zweidimensionalen Rauschen zuwenden. Während wir im eindimensionalen Raum entlang einer Linie zwischen zwei Punkten (
`fract(x)` und `fract(x)+1.0`) interpolieren, geschieht dies im zweidimensionalen Raum zwischen den vier Eckpunkten eines Vierecks innerhalb einer Ebene (`fract(st)`, `fract(st)+vec2(1.,0.)`, `fract(st)+vec2(0.,1.)` und `fract(st)+vec2(1.,1.)`).In ähnlicher Weise müssen wir im dreidimensionalen Raum vorgehen. Hier sind es die acht Ecken eines Kubus, zwischen denen es zu interpolieren gilt. Dabei dreht sich alles um die Interpolation von Zufallswerten (engl. random values), weshalb man in diesem Zusammenhang auch von Value Noise spricht.
Wie schon bei unserem Beispiel für den eindimensionalen Raum, erfolgt die Interpolation auch hier nicht linear, sondern kubisch, damit ein sanfter Verlauf zwischen allen Punkten des viereckigen Rasters entsteht.
Schau Dir die folgende Noise-Funktion an.
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Wir „blasen“ den Raum zunächst auf das Fünffache auf (Programmzeile 45), damit wir die Interpolation zwischen den einzelnen Elementen unseres Rasters besser erkennen können. Innerhalb der Noise-Funktion erfolgt dann die Einteilung des Raumes in einzelne Zellen. Wir speichern die Integer-Position der Zelle, ebenso den Nachkommateil für die Position innerhalb der Zelle. Die Integer-Position nutzen wir, um einen Zufallswert für alle vier Eckpunkte der Zelle zu erhalten (Programmzeilen 23-26). Schließlich interpolieren wir in der Programmzeile 35 zwischen den vier zufällig gewählten Eckpunkten auf Basis des Nachkommateils, den wir zuvor gespeichert haben.
Jetzt bist Du wieder dran. Versuche Dich an den folgenden Aufgaben:
Ändere den Multiplikationsfaktor in Programmzeile 45*. Versuche ihn zu animieren.
* Versuche herauszufinden, ab welcher Vergrößerungsstufe das Rauschen wieder vollkommen zufällig und unzusammenhängend (chaotisch) wirkt.
* Ab welcher Vergrößerungsstufe ist das Rauschen nicht mehr wahrnehmbar?
* Versuche die Noise-Funktion sinnvoll mit den Mauskoordinaten in Verbindung zu bringen.
* Was wäre, wenn wir den Farbverlauf des Rauschens als Distanzfeld behandeln? Versuche etwas Interessantes damit anzufangen.
* Nun, da wir ein gewisses Maß an Kontrolle über Ordnung und Chaos erzielt haben, ist es an der Zeit, dieses Wissen umzusetzen. Erstelle eine Komposition aus Rechtecken, Farben und Rauschen, die an die Struktur eines Kunstwerks von Mark Rothko erinnert.
Rauschen und seine Verwendung für generative Designs
Noise-Algorithmen wurden ursprünglich entwickelt, um digitalen Texturen ein natürliches Aussehen zu verleihen. Die ein- und zweidimensionale Implementation, die wir bislang betrachtet haben, verwendet dafür Interpolationen zwischen Zufallswerten (engl. random values), weshalb sie als Value Noise bezeichnet wird. Doch es gibt noch andere Verfahren, um Rauschen zu erzeugen.
Wie die vorangegangenen Abschnitte gezeigt haben, tendiert Value Noise zur Erzeugung blockartiger Strukturen. Um diesen Blockeffekt zu reduzieren, hat Ken Perlin im Jahre 1985 einen weiteren Algorithmus mit Namen Gradient Noise entwickelt. Ken hatte herausgefunden, wie man zwischen zufälligen Gradienten (Verläufen) interpoliert, anstatt zwischen einfachen Zufallszahlen. Diese Gradienten waren das Ergebnis einer 2D-Zufallsfunktion, die Richtungsvektoren (bei GLSL in Form eines
`vec2`) anstelle einfacher Fließkommazahlen (`float`) zurückliefert. Klicke auf das folgende Bild, um einen Blick auf den dahinterstehenden Programmcode zu werfen und zu verstehen, wie das Ganze funktioniert.Nimm Dir einen Moment Zeit, um die beiden folgenden Beispiele von Inigo Quilez zu studieren und richte Deine Aufmerksamkeit dabei besonders auf die Unterschiede zwischen Value Noise und Gradient Noise.
Wie ein Maler, der genau weiß, wie die Farben auf seiner Staffelei miteinander harmonieren, werden auch wir die Noise-Funktionen umso besser nutzen können, je mehr wir deren Vorgehensweise verstehen. Wenn wir beispielsweise eine zweidimensionale Noise-Funktion nutzen, um den Raum zu drehen, während wir gerade Linien zeichnen, entsteht der folgende Dreheffekt, der stark an die Maserung von Holz erinnert. Auch hier kannst Du wieder auf die Grafik klicken, um der den Shader-Code dahinter anzuschauen.
glslpos = rotate2d( noise(pos) ) * pos; // drehe den Raum
pattern = lines(pos,.5); // zeichne Linien
Ein anderer Weg zur Erzeugung interessanter Muster mit Hilfe von Noise-Funktionen besteht in ihrer Behandlung als Distanzfeld und der Anwendung einiger Tricks, die wir im Kapitel über Formen kennengelernt haben.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // schwarze Spritzer
color -= smoothstep(.35,.4,noise(st*10.)); // Löcher in den Spritzern
Ein dritter Weg basiert auf der Modulation einer Form mit Hilfe einer Noise-Funktion. Auch dabei kommen einige der Techniken zum Einsatz, die wir im Kapitel über Formen kennengelernt haben.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Empfohlene Übungen:
* Welche anderen generativen Designs fallen Dir ein? Was ist mit Granitgestein? Marmor? Magma? Wasser? Suche Dir drei Fotos entsprechender Materialien und entwickle die zugehörigen Algorithmen unter Einbeziehung von Noise-Funktionen.
* Nutze Noise-Funktionen, um eine Form zu modulieren.
* Wie steht es um die Nutzung von Noise-Funktionen für Bewegungsabläufe? Schlage noch einmal im Kapitel über Matrizenoperationen nach. Nutze das dortige Beispiel, in welchem das Kreuz über die Zeichenfläche bewegt wird, und füge dem Ganzen etwas Zufall und Noise hinzu.
* Erzeuge ein generatives Design wie bei Jackson Pollocks Bildern.
Verbesserte Noise-Funktionen
Eine Verbesserung von Perlin an seinem ursprünglichen Non-Simplex Noise-Algorithmus hin zu einem Simplex Noise ist der Ersatz der kubischen Hermite-Funktionn ( _f(x) = 3x^2-2x^3_, die der
`smoothstep()`-Funktion in GLSL entspricht) durch eine quintische Interpolationsfunktion ( _f(x) = 6x^5-15x^4+10x^3_ ).Diese lässt die Interpolationskurve an ihren Enden flacher werden, so dass sie nahtloser in die nächste Interpolationskurve übergeht. Man erhält dadurch kontinuierlichere Übergänge zwischen den einzelnen Zellen. Du kannst Dir ein Bild davon machen, indem Du die Kommentare an der zweiten Formel in dem folgenden Graphen entfernst. (Beide Graphen in einem Bild siehst Du auch hier). Entscheidend ist wie immer der Bereich zwischen
`0.0` und `1.0` auf der x-Achse.
<div class="simpleFunction" data="
// Kubische Hermite-Funktion wie bei SmoothStep()
y = xx(3.0-2.0*x);
// Quintische Interpolationskurve
//y = xxx(x(x*6.-15.)+10.);
"></div>
Beachte, wie die beiden unterschiedlichen Kurven an ihren Endbereichen voneinander abweichen. Mehr dazu erfährst du in Ken s eigenen Worten hier.
Simplex Noise
Für Ken Perlin war der Erfolg seines ersten Noise-Algorithmus noch nicht genug. Er suchte weiter nach Verbesserungen. Auf der berühmten Grafik-Konferenz Siggraph präsentierte er im Jahre 2001 den „Simplex Noise“-Algorithmus, mit dem er die folgenden Optimierungen gegenüber dem Vorgänger erzielte:
* Geringerer Rechenaufwand und weniger Multiplikationen.
* Ein Rauschen, das sich auch für höhere Dimensionen berechnen lässt, ohne dass der Aufwand ins Astronomische steigt.
* Ein Rauschen ohne sichtbare Richtungsartefakte.
* Ein Rauschen mit sauber definierten und nahtlosen Übergangen, das sich einfach berechnen lässt.
* Ein Algorithmus, der auch in der Hardware einfach zu implementieren ist.
Ich weiß, was Du jetzt denkst... „Wow, wer ist dieser Mann?“ Ja, seine Leistungen sind wirklich großartig. Aber mal ganz im Ernst, wie ist es ihm gelungen, seinen Algorithmus zu verbessern? Nun, wir haben gesehen, dass er zur Berechnung des Rauschens in zwei Dimensionen zwischen den vier Eckpunkten eines Vierecks interpoliert. Man darf deshalb zurecht davon ausgehen, dass er für die Berechnung in drei Dimensionen (eine Implementierung in GLSL findest Du hier) und vier Dimensionen zwischen 8 bzw. 16 einzelnen Eckpunkten interpolieren muss. Korrekt?
In anderen Worten: Für die Berechnung des Rauschens in N Dimensionen muss man zwischen 2 hoch N (_2^N_) verschiedenen Punkten interpolieren. Ken war jedoch aufgefallen, dass die offensichtliche „Bauweise“ für eine flächenfüllende Form zwar fraglos ein Viereck darstellt, die einfachste Form der Abdeckung jedoch mit gleichseitigen Dreiecken erzielt wird. Deshalb ersetzte er das rechtwinklige Gitternetz durch ein Netz aus gleichseitigen Dreiecken.
Mit gleichseitigen Dreiecken besteht die einfachste Form für N Dimensionen aus einer Konstruktion mit N+1 Eckpunkten. Das bedeutet einen Eckpunkt weniger für zweidimensionales Rauschen, vier Eckpunkte weniger für dreidimensionales Rauschen und elf Eckpunkte weniger für vierdimensionales Rauschen. Diese „Ersparnis“ bringt einen enorm reduzierten Rechenaufwand und dadurch einen großen Geschwindigkeitszuwachs mit sich!
In zwei Dimensionen erfolgt die Interpolation genau wie bei der Standard Noise-Funktion. Weil wir aber ein gleichseitiges Dreieck nutzen, müssen wir dabei nur drei Punkte einbeziehen.
Doch wie wird dieses vereinfachte Gitternetz aufgebaut? In einem weiteren brillanten und äußerst eleganten Schritt, werden die Zellen des viereckigen Gitternetzes jeweils in zwei gleichschenklige Dreiecke (mit jeweils zwei gleich langen Seiten) aufgeteilt. Anschließend wird das Netz so verzerrt, dass alle Dreiecke auch gleichseitig sind (also mit jeweils drei gleich langen Seiten). Denn damit lässt sich am einfachsten interpolieren.
Anschließend fahren wir fort, wie es Stefan Gustavson in seinen Ausführungen beschreibt: "... indem wir uns die ganzzahligen Anteile der transformierten Koordinaten (x,y) des zu berechnenden Punktes anschauen, denn darüber können wir leicht feststellen, zu welchen zwei Dreiecken der Punkt gehört. Indem wir die Werte von x und y vergleichen, erfahren wir, ob sich der Punkt in dem oberen oder in dem unteren Dreieck befindet. So können wir die drei korrekten Eckpunkte in die Berechnung einbeziehen.“
Im folgenden Programmcode kannst Du die Kommentarzeichen aus der Programmzeile 44 entfernen, um zu sehen, wie das Gitternetz gezerrt wird. Und sobald Du die Kommentierung von Zeile 47 aufhebst, erkennst Du die Aufteilung der Fläche in gleichschenklige Dreiecke. Beachte, wie wir in Zeile 22 das verzerrte Rechteck einfach in zwei gleichseitige Dreiecke aufteilen, indem wir testen, ob
`x > y` ist („unteres“ Dreieck) oder `y > x` („oberes“ Dreieck).<div class="codeAndCanvas" data="simplex-grid.frag"></div>
All diese Verbesserungen münden in das Meisterwerk, das als Simplex Noise-Algorithmus bekannt ist. Der folgende Programmcode ist eine Implementation dieses Algorithmus in GLSL, die von Ian McEwan und Stefan Gustavson entwickelt wurde (hier nachzulesen). Für unsere Lernzwecke ist diese theoretische Erklärung vielleicht etwas zu aufwändig. Allerdings wirst Du beim Anklicken der Grafik und einem Blick auf den dahinterstehenden Programmcode feststellen, dass dieser weniger kryptisch daherkommt, als Du vielleicht erwartet hast. Außerdem ist der Code vergleichsweise kompakt und kann schnell ausgeführt werden.
Nun aber genug der technischen Spitzfindigkeiten. Es ist an der Zeit, dass Du den Simplex Noise-Algorithmus als eine wertvolle Ressource im Rahmen Deiner eigenen Projekte zum Einsatz bringst:
* Versuche das Besondere an den verschiedenen Implementationen von Noise zu erkennen. Stelle Sie Dir wie Rohmaterialien vor, aus denen ein Bildhauer seine Skulptur herausschält. Wie fühlen sich die unterschiedlichen Ergebnisse für Dich an? Verziehe beim Betrachten deine Augen, um der Vorstellungskraft Raum zu geben, so wie Du die Wolken nach Mustern absuchst. Was erkennst Du? Woran erinnert es Dich? Was könnte man wohl mit den verschiedenen Noise-Implementationen anstellen? Höre auf Deine innere Stimme und probiere es einfach aus!
* Entwickle einen Shader, der die Illusion einer Strömung von Flüssigkeiten erweckt. Etwa wie bei einer Lava-Lampe, Tintentropfen, Wasser etc.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Nutze den Simplex Noise-Algorithmus um Deinen bisherigen Arbeiten mehr Textur zu verleihen.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
In diesem Kapitel haben wir versucht, Kontrolle über das Chaos zu erlangen. Das war keine leichte Aufgabe. Es braucht seine Zeit, um ein Meister des Rauschens und der Raumkrümmung zu werden.
In den folgenden Kapiteln werden wir einige bekannte Techniken aufgreifen, mit Denen Du deine Fähigkeiten weiter verbessern kannst. Bis dahin genieße ein wenig Zeit draußen an der frischen Luft und lasse Dich von den Mustern und Strukturen der Natur inspirieren. Denn Deine Fähigkeiten zur Beobachtung müssen ähnlich gut entwickelt sein, wie Deine Programmierfähigkeiten. Vielleicht sogar noch besser. Genieße die wertvolle Zeit abseits des Computers!
<p style="text-align:center; font-style: italic;">"Sprich mit den Bäumen und werdet Freunde." Bob Ross
</p>
---
11/README Fa (11/README-fa.md)
نویز
وقت استراحت است! با تابع های رندومی که مانند برفک ها تلویزیون بودند کار کردیم و هنوز سرمان گیح میرود و چشمانامان خسته شده است:) وقتش هست که کمی قدم بزنیم!
ما روی پوست خود هوا و خوشید را روی صورتمان حس میکنیم. جهان اینگونه زنده و غنی است. رنگ ها، صدا ها و بافت ها. وقتی راه میرویم نمیتوانیم از سطوح جاده و سنگ ها و درختان و ابرها چشم پوشی کنیم.
به غیر قابل پیش بینی بودن این رنگ ها رندوم گفته میشود. اما مانند رندومی که در دروس قبلی کار کردیم نیستند. جهان جایی غنی و پیچیده است! چگونه میتوان این تنوع را از نظر محاسباتی پیاده سازی کرد؟
این سوالی بود که Ken Perlin قصد داشت در اوایل دهه 1980 آن را حل کند، وقتی که وظیفه داشت بافتی واقعی تر برای فیلم ترون ایجاد کند. برای این منظور اون الگوریتمی ایجاد کرد که موجب برنده شدن جایزه اسکار شد.
کد زیر نمونه اصلی نویز پرلین نیست اما برای شروع و درک این مبحث مناسب است.
<div class="simpleFunction" data="
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i); //rand() is described in the previous chapter
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
در این خطوط کاری مشابه قسمت های قبلی میکنیم. ما عدد ادامه دار یعنی (
`x`) را تبیدل به اعداد صحیح یعنی (`i`) و مقدار کسری اش (`f`) میکنیم. از `floor()` برای ساخت `i` و `fract()` برای ساخت `f` استفاده میکنیم. بعد تابع `rand()` را به قسمت صحیح `x` اعمال میکنیم, که در نتیجه یک عدد رندوم خاص برای هر مقدار صحیح میدهد.بعد از این ها ما دو خط کامنت شده داریم. اولی بین دو تابع رندوم بصورت خطی عمل درون یابی انجام میدهد..
glsly = mix(rand(i), rand(i + 1.0), f);
این خط را از حالت کامت خارج کنید و ببینید چه شکلی میشود. از مقادیر
`fract()` ذخیره شده در f برای `mix()` درون یابی بین دو مقدار رندوم استفاده میکنیم.اما میتوانیم کاری بهتر از درون یابی خطی انجام دهیم.
حالا خطی که از
`smoothstep()` استفاده میکند را از حالت کامنت خارج کنید. glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
در این کد میبیندی که انتقال بین قله ها نرم تر صورت گرفته است.در برخی پیاده سازی های نویز برنامه نویسان تمایل دارند نویز مکعبی خود را پیاده سازی کنند (مانند فرمول زیر) به جای استفاده از `smoothstep()`.glslfloat u = f f (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation
این رندوم نرم یک عنصر اساسی برای مهندسان گرافیک و آرتیست ها است و این امکان را میدهد که تصاویر و اشکال با حسی اورگانیک ساخته شوند. الگوریتم نویز پرلین بار ها در زبان ها و ابعاد مختلف پیاده سازی شده است تا قطعاتی مسحور کننده برای انواع استفاده های خلاقانه بسازد.
حالا نوبت شماست:
* تابع نویز
`float noise(float x)` مخصوص خودتان را بسازید.* از تابع نویز خود برای انیمیت کردن، تغییر زاویه و اندازه شکل استفاده کنید.
* یک ترکیب متحرک از اشیایی بسازید که در حال رقصیدن باشند.
* یک شکل خاص و استثنایی با استفاده از نویز بسازید.
* هنگامی که شکلتان را ساختید سعی کنید با اضافه کردن حرکتی خاص آن را بیشتر به یک شخصیت تبدیل کنید.
نویز دو بعدی
حال که میدانیم چگونه در یک بعد نویز بسازیم, وقتش است به سراغ دو بعدی ها برویم. در دو بعدی, به جای درون یابی بین دو نقظه از یک خط (
`fract(x)` و `fract(x)+1.0`), باید بین 4 گوشه یک صفحه درون یابی کنیم (`fract(st)`, `fract(st)+vec2(1.,0.)`, `fract(st)+vec2(0.,1.)` و`fract(st)+vec2(1.,1.)`).به طور مشابه، اگر بخواهیم نویز سه بعدی ایجاد کنیم باید بین 8 گوشه یک مکعب درون یابی کنیم. این تکنیک تمام در مورد درون یابی بین مقادیر است، به همین علت هم به آن نویز مقدار گفته میشود.
مانند مثال یک بعدی، این درون یابی بطور خطی نیست بلکه بصورت cubic است، به طوری که هر نقطه داخل محدوده مان را درون یابی میکند.
به این تابع نویز نگاهی بیاندازید.
<div class="codeAndCanvas" data="2d-noise.frag"></div>
اول فضا را 5 برابر کردیم(در خط 45) تا درون یابی بین مربع هاقابل مشاهده باشند. سپس در تابع نویز, فضا را مانند سلول هایی در نظر گرفتیم. همچنین مقدار صحیح سلول ها و مقدار کسری سلول ها را ذخیره کرده ایم. از مختصاصت صحیح برای محاسبه مقادیر رندوم گوشه ها استفاده میکنیم، برای هر گوشه مقداری را بدست میآوریم(در خطوط 23 تا 26) در نهایت در خط 35 با استفاده از مقادیر کسری هر سلول، درون یابی بین چهار گوشه را انجام میدهیم.
حالا نوبت شماست، این تمرین ها را انجام دهید:
* در خط 45 ضرب را تغییر دهید. سعی کنید آن متحرک کنید.
* در چه مرحله از زوم دوباره نویز شبیه رندوم میشود؟
* تا چه مقداری از زوم نویز محسوس نیست؟
* سعی کنید این تابع نویز را با مختصات موس پیوند بزنید.
* چه میشود اگر طیف نویز را مثل یک میدان فیلد در نظر بگیریم؟ یک کار جالب با آن انجام دهید.
* حال که بین نظم و هرج و مرج به کنترلی رسیدیم، وقتش است از دانشمان اسفاده کنیم. ترکیبی از مستطیل ها درست کنید, سعی کنید رنگ بندی و پیچیدگی های نقاشی Mark Rothko را بوجود آورید.
استفاده از نویز در طراحی ها مولد
الگوریتم های نویز برای طبیعی تر جلوه دادن تصاویر بوجود آمدند. نویز های یک بعدی و دو بعدی که فعلا دیدیم، درون یابی بین چند مقدار بودند، به همین علت هم به آنان نویز مقدار گفته میشود، اما روش های دیگری نیز برای ساختن نویز وجود دارد.
همانطور که در تمرین های قبلی متوجه شدید, نویز های مقدار تمایل به تکه تکه بودن بلاکی دارند." برای کاهش تکه تکه ای بودن نویز, در 1985 کن پرلین نوعی دیگر از الگوریتم نویز را به نام Gradient Noise خلق کرد. اون فهمید چگونه بجای مقادیر، بین شیب ها یا همان Gradient ها درون یابی کند. این گرادیان ها نتیجه یک رندوم دو بعدی که جهت را (represented by a
`vec2`) به جای یک مقدار (`float`) برمیگرداند است. بر روی عکس پایین کلیک کنید و ببیند که چگونه کار میکند.چند دقیقه به مثال های Inigo Quilez و به تفاوت بین این دو نویز value noise و gradient noise توجه کنید.
مانند نقاش که میداند رنگ دانه ها چگونه عمل میکنند، ما نیز هرچه بیشتر در مورد نحوه پیاده سازی نویز بدانیم، بهتر میتوانیم از آن استفاده کنیم. برای مثال, اگر ما از نویز دوبعدی برای چرخش فضای در جایی که خط های مستقیم رندر شده اند استفاده کنیم، میتوانیم افکت چرخشی زیر که شبیه تکستچر چوب است را پیاده سازی کنیم. میتوانید با کلیک روی تصویر زیر کد آن را مشاهده کنید
glslpos = rotate2d( noise(pos) ) * pos; // rotate the space
pattern = lines(pos,.5); // draw lines
راه جالب دیگر برای ساخت پترن، رفتار کردن با انان به عنوان فیلد فاصله است. چند ترفند Shapes chapter را میتوان روی آن پیاده کنید.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Black splatter
color -= smoothstep(.35,.4,noise(st*10.)); // Holes on splatter
راه دیگر اسفاده از نویز ها تعدیل کردن اشکال است. که نیاز به استفاده از تکنیک هایی دارد که در فصل chapter about shapes یاد گرفتیم.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
برای تمرین:
* چه پترن های مولد دیگری میتوانید بسازید؟ سنگ گرانیت؟ مرمر؟ گدازه؟ آب؟ سه تصویر دلخواه را که میخواهید با نویز پیاده سازی کنید انتخاب و سعی کنید با استفاده از الگوریتم ها آنان را پیاده سازی کنید.
* از نویز برای تعدیل یک شی استفاده کنید.
* اگر از نویز برای حرکت و جنبش استفاده کنیم چه? نگاهی به فصل Matrix chapter بیاندازید. از تابع انتقالی که "+" را میچرخواند استفاده کنید, و نویز و رندوم به آن اضافه کنید.
* سعی کنید یکی از اثر های Jackson Pollock را بازسازی کنید.
بهبود نویز
یکی از بهبود های پریل برای نویز ساده اش، تعویض منحنی cubic Hermite ( _f(x) = 3x^2-2x^3_ , که مانند
`smoothstep()` هست) با منحنی quintic interpolation ( _f(x) = 6x^5-15x^4+10x^3_ ) است. این کار باعث میشود دو انتهای منحنی مسطح تر باشند، پس هر حاشیه با ظرافت بیشتر و بهتری به مرز بعدی متصل میشود . به زبان دیگر, یک انتقال مداوم و نرم تر بین سلول ها خواهید داشت. میتوانید در مثال زیر این دو را با هم مقایسه کنید (یا دو معادله را در اینجا مشاهده کنید).<div class="simpleFunction" data="
// Cubic Hermite Curve. Same as SmoothStep()
y = xx(3.0-2.0*x);
// Quintic interpolation curve
//y = xxx(x(x*6.-15.)+10.);
"></div>
دقت کنید چگونه انتهای بردار ها تغییر میکند. برای اطلاعات بیشتر در این زمینه Ken's own words.
Simplex نویز
برای پرلین، موفقیت این الگوریتم کافی نبود. او فکر میکرد بهتر از این ها میتواند انجام دهد. در جشنواره سیگراف سال 2001 او samplex noise را معرفی کرد، که منجرب به این پیشرفت ها نسبت به الگوریتم قبلی شده بود:
* الگوریتم با پیچیدگی محاسباتی و عملیات ضرب کمتر.
* نویزی که با ابعاد بالاتر و همزمام هزینه کمتر قابل اجرا بود.
* نویزی بدون مصنوعات جهت دار.
* نویزری با گرادیانت(شیب) های مشخص و مداوم که بسیار ارزان قابل محاسبه است.
* الگوریتم به راحتی روی سخت افزار قابل اجرا است.
میدانم الان چه فکری میکنید... "این مرد دیگر کیست؟" بله، کار های او فوق العادست! اما جدا, چگونه الگوریتم را تقویت کرد؟ ، خب دیده بودیم چگونه در دو بعد بین 4 نقطه درون یابی میکردیم (گوشه های مربع); پس میتوان حدس زد برای سه بعد (پیاده سازی شده در اینجا) و چهار بعدی ما به 8 و 16 نقطه برای درون یابی نیاز داریم. درست است؟ به عبارتی برای n بعد به دو بتوان n درون یابی نیاز داریم. بن این موضوع را متوجه شده بود، واضح است که راحترین شکل برای پر کردن فضا مربع است, ام ساده ترین شکل دو بعدی، مثلث متساوی الاضلاع است. پس شروع به تعویض صفحه مختصات مربعی (که یاد گرفتیم چگونه کار میکند) با یک مختصات ساده شده مثلث متساوی الاضلاع کرد.
ساده ترین شکل در فضای n بعدی، شکلی است با n+1 گوشه. در نتیجه نقاط کمتر برای درون یابی در دوبعدی و سه بعدی و چهار بعدی مورد استفاده قرار میگیرد! این یک بهبود بزرگ است!
در دوبعدی این درون یابی شبیه الگوریتم نویز معمولی است، همان درون یابی مقدار گوشه ها. با این تفاوت که، با استفاده از مختصات ساده شده، کافیست بین سه نقطه درون یابی کنیم.
مختصات ساده شده تا به اینجا چگونه بود؟ در یک اقدام هوشمندانه دیگر این مختصات ساده را میتوان از صفحه بندی معمولی 4 گوشه ای بدست آورد. به طوری که هر 4 گوشیه شامل دو مثلث متقارن هستن و با کج کردن آن ها میتوان آنان را به مثلث متساوی الاضلاع تبدیل کرد.
Stefan Gustavson, در این مقله میگوید:: _".با نظر به قسمت های صحیح مختصات تبدیل شده برای نقطه ای که میخواهیم ارزیابی کنیم، میتوانیم سریع محاسبه کنیم که کدام سلول حاوی نقطه مورد نظر اسj. il]kdk fh lrhdsi lrhndv x,y میتوانیم تعیین کنیم که آیا این تقطه بالای سلول است یا پایین آن."_
در مثال زیر خط 44 را از حالت کامنت در بیاورید تا ببنید مختصات چگونه کشیده میشود, و بعد خط 47 را از حالت کامنت خارج کنید تا ببنید صفحه بندی simplex چگونه ساخته میشود. توجه کنید که چگونه در خط 22 مربع کج شده را به دو مثلث متساوی الاضلاع تقسیم میکنیم، ان هم فقط با استفاده از if
`x > y` (مثلث پایینی) or `y > x` (مثلث بالایی).<div class="codeAndCanvas" data="simplex-grid.frag"></div>
نتیجه تمام این پیشرفت ها الگوریتمی شد به نام Simplex Noise. در ادامه بصورت GLSL این الگوریتم توسط Ian McEwan وStefan Gustavson پیاده سازی شده است (ارائه شده در این مقاله) که البته برای مقاصد آموزشی بیش از حد پیچیده است,البته خوشحال باشید در کد زیر به صورت ساده تر و سریع تر این الگوریتم قابل مشاهده است.
خب بحث های تکنیک دیگر کافیست, حال زمان آن است از این منابع برای کار های خود استفاده کنید:
* نگاه کنید که هر نویز به چه شکلی هست. آن ها را به مانند ماده اولیه ای مثل سنگ مرمر برای مجسمه ساز تصور کنید. چه نظری در مورد هر کدام دارید؟ چه حسی به شما میدهند? چشم هایتان بمالید و تخیلاتتان را تحریک کنید، مانند زمانی که میخواهید اشکالی از ابر ها پیدا کنید. چه چیزی میبینید? شما را یاد چه می اندازد? چه تصوری میتوانید بکنید که از هر جزئ نویز چه میتوان ساخت? دنبال ندای درونتان بروید و سعی کنید کد آن را پیاده کنید.
* شیدری بسازید که توهم جریان را نشان دهد. مثل چراغ لاوا, قطره جوهر, آب, و غیره.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* از Simplex Noise برای اضافه کردن تکتچر به کار هایی که تا امروز کرده اید استفاده کنید.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
در این فصل مباحثی در مورد کنترل هرج و مرج داشتیم. کار راحتی نبود! حرفه ای شدن در نویز های زمان و تمرین زیادی میطلبد.
در فصل بعدی برخی دیگر از تکنیک های شناخته شده برای تکمیل مهارت های تان و استفاده بیش از پیش از نویز ها برای طراحی شیدر های با کیفیت را خواهیم دید. تا آن زمان از الگو های پیچیده نویز لذت ببرید. توانایی مشاهده شما نیز به اندازه مهارت ساختن، نیاز به تمرین دارد، بیرون بروید و از بقیه روز لذت ببرید!
<p style="text-align:center; font-style: italic;">"Talk با درختان حرف بزنید و با آنان دوست شوید." Bob Ross
</p>
---
11/README Fr (11/README-fr.md)
Bruit
Il est temps de faire une pause !
Nous avons joué avec des fonctions aléatoires qui ressemblaient au bruit blanc d'une télévision.
On a encore la tête qui tourne en pensant aux shaders et nos yeux sont fatigués.
Il est grand temps de sortir faire un tour !
Nous sentons l'air sur notre peau, le soleil dans les yeux, le monde est tellement vibrant et coloré.
Les couleurs, les textures, les sons. Pendant notre balade, nous ne pouvons nous empêcher de remarquer la surface de la route, les rochers, les arbres et les nuages.
Le côté imprévisible des variations de ces textures pourrait s'apparenter à de l'aléatoire mais elles semblent loin des fonctions aléatoires que nous avons vues juste avant.
Le monde est tellement complexe, comment pouvons nous programmer un shader pour nous rapprocher de cette immense variété ?
Cette question fut posée à Ken Perlin en 1980 quand il fut missionné pour trouver un moyen de générer des textures plus réalistes pour le film "Tron".
Le résultat de ses recherches fut un élégant algorithme de bruit qui fut récompensé par un Oscar (rien que ça).
Ce qui suit n'est pas l'algorithme de Perlin mais il donne les clés pour comprendre comment générer du bruit à partir de valeurs aléatoires.
<div class="simpleFunction" data="
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i); //rand() is described in the previous chapter
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
Nous commençons par quelque chose d'assez proche de ce que nous avons fait au chapitre précédent.
Nous récupérons les parties entières (
`i`) et fractionnelles (`f`) de `x`.
Nous utilisons `floor()` pour obtenir `i` et `fract()` pour obtenir `f`.
Ensuite nous appelons `rand()` sur la partie entière de `x`, ce qui renvoie une valeur unique par entier.Après quoi, vous voyez deux lignes commentées, la première fait une interpolation linéaire entre les deux valeurs
`rand(i)` et `rand(i+1)text
glsl
y = mix(rand(i), rand(i + 1.0), f);
mix()`) les deux valeurs aléatoires.Nous savons qu'il existe différentes manières d'interpoler des valeurs entre 0 et 1.
Décommentez la ligne suivante qui utilise un
`smoothstep()` pour calculer l'interpolation au lieu de l'interpolation linéaire.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Vous devriez constater que la transition entre pics est beaucoup plus souple.
Dans certaines implémentations, vous verrez que les programmeurs préfèrent parfois coder leur propre fonction cubique (comme la formule suivante) au lieu d'utiliser
`smoothstep()`.glslfloat u = f f (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation
Cet aléatoire lissé change tout pour les artistes et les designers ; il permet de générer des images au rendu plus naturel.
L'algorithme de Perlin a été implémenté dans tous les langages et en plusieurs dimensions pour créer toutes sortes de motifs.
A votre tour :
* Créez votre propre fonction
`float noise(float x)`.
* Utilisez la pour animer une forme sur la scène, la faire pivoter ou la redimensionner.
Faites danser plusieurs formes avec cette fonction de bruit*.
* Construisez des formes organiques avec une fonction de bruit.
Une fois que vous avez une créature*, essayez de la développer en lui donnant des mouvements particuliers.Bruit 2D
A présent que nous savons faire un bruit sur une dimension, passons à la 2D.
En 2D, au lieu d'interpoler entre deux points sur une ligne, (
`floor(x)` et `floor(x)+1.0`),
nous allons interpoler entre les quatre coins d'un carré du plan: `floor(st)`, `floor(st)+vec2(1.,0.)`, `floor(st)+vec2(0.,1.)` et `floor(st)+vec2(1.,1.)`.De la même façon que si nous voulons un bruit en 3D, nous devrons créer une interpolation entre les huit coins d'un cube.
Cette technique se base sur l'interpolation de valeurs c'est pourquoi on l'appelle bruit de valeurs (value noise).
Comme dans l'exemple à une dimension, nous n'utiliserons pas une interpolation linéaire mais cubique qui nous permettra de lisser les interpolations dans notre grille carrée.
Regardez la fonction de bruit suivante :
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Nous commençons par redimensionner l'espace en le multipliant par 5 (ligne 45), de manière à mieux voir les interpolations de la grille.
Ensuite, dans la fonction de bruit, nous subdivisons l'espace en cellules.
Nous récupérons les valeurs entières et fractionnelles de la cellule.
La valeur entière nous permet de créer les valeurs aléatoires des quatre coins du carré (lines 23-26).
Enfin, ligne 35, nous interpolons entre les quatre valeurs aléatoires des coins en utilisant la valeur fractionnelle du fragment dans la cellule.
A votre tour, essayez les choses suivantes :
* Changez le multiplicateur ligne 45, essayez de l'animer.
* A quel niveau de zoom le bruit commence-t'il à ressembler à de l'aléatoire ?
* A quel niveau de zoom le bruit devient-il imperceptible ?
* Essayez de brancher la souris pour ajuster le niveau de zoom.
* Que se passe-t'il si on utilise le dégradé du bruit comme un champ de distances ? Essayez de trouver un usage intéressant.
* Maintenant que vous contrôlez un peu mieux l'ordre et le chaos, essayez d'utiliser tout ce que vous savez pour créer une composition de rectangles, de couleurs et bruit ressemblant à une peinture de Mark Rothko.
Utilisation du bruit dans le design génératif
Les algorithmes de bruit ont été inventés pour apporter une touche plus naturelle aux textures digitales.
Les implémentations 1D et 2D que nous avons vues jusqu'à présent étaient des interpolations entre valeurs aléatoires, d'où leur nom de bruit de valeurs (value noise),
mais il existe d'autres manières de créer un bruit...
Comme vous l'avez découvert dans les exercices précédents, les bruits de valeurs (value noise) paraissent souvent crénelés.
Pour atténuer le crénelage, Ken Perlin développe en 1985 une autre implémentation de son algorithme appelée Bruit de Gradient.
Ken a trouvé comment interpoler des dégradés plutôt que des valeurs.
Ces dégradés sont le résultat d'une fonction aléatoire 2D qui renvoie une direction (représentée par un
`vec2`) au lieu de renvoyer une valeur de type `float` (un chiffre).
Cliquez sur le lien ci dessous pour de plus amples informations sur l'algorithme.Prenez un moment pour comparer ces deux exemples par Inigo Quilez et soyez attentifs aux différences entre bruit de valeurs et bruit de gradient.
Comme un peintre qui comprend comment marchent les pigments, plus vous en saurez sur les différentes implémentations de bruit, mieux vous saurez vous en servir.
Par exemple, si on utilise un bruit 2D pour faire pivoter un espace sur lequel on rend des lignes droites, on obtiendra le motif suivant qui ressemble à du bois.
Cliquez sur les exemples pour voir à quoi ressemble le code :
glslpos = rotate2d( noise(pos) ) * pos; // fait pivoter l'espace
pattern = lines(pos,.5); // puis dessine des lignes
Une autre approche pour créer des motifs consiste à les considérer comme des champs de distances et à leur appliquer différents traitements décrits au chapitre des formes.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // éclaboussure noire
color -= smoothstep(.35,.4,noise(st*10.)); // 'trou' dans l'éclaboussure
Une troisième approche consiste consiste à moduler une forme. Cela fait également appel aux techniques vues au chapitre des formes.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Entraînement :
* Quel autre motif génératif pouvez vous faire avec du bruit ? Marbre ? Magma ? Eau ? Trouvez des images de textures qui vous intéressent et implémentez les avec du bruit.
* Modulez une forme avec du bruit.
Peut-on utiliser du bruit pour donner du mouvement ? Retournez au chapitre des transformations. Utilisez l'exemple des translations qui déplace la croix et appliquez lui de l'aléatoire et du bruit*.
* Créez un Pollock génératif.
Bruit simplexe (Simplex Noise)
Malgré son succès, Ken Perlin n'était toujours pas satisfait de son algorithme, il pensait qu'il pouvait aller plus vite.
Au Siggraph 2001, il présenta le bruit simplexe qui lui permit d'apporter les améliorations suivantes:
* Un algorithme ayant une complexité computationnelle moindre et moins de multiplications.
* Un bruit qui passe aux dimensions supérieures avec un coût computationnel moindre.
* Un bruit sans artefacts directionnels.
* Un bruit bien défini, avec des dégradés continus et pouvant se calculer à moindre frais.
* Un algorithme facile à implémenter matériellement.
Je sais ce que vous pensez... "Mais qui est cet homme ?!" Oui, son travail est fantastique ! Mais concrètement, comment a-t'il amélioré l'algorithme ?
Bien, nous avons vu comment, pour 2 dimensions, il interpolait entre les valeurs de 4 points (les quatre coins d'un carré).
Donc on peut imaginer que pour trois (voir implémentation ici) et quatre dimensions, nous aurons besoin de 8 et 16 valeurs pour faire nos interpolations.
En d'autres mots, pour N dimensions, il nous faudra 2 puissance N (2^N) valeurs à interpoler.
Ken a remarqué que, bien qu'on pense immédiatement au carré comme forme de remplissage pour un espace 2D, la forme 2D la plus simple est le triangle équilatéral.
Il commença donc à remplacer la grille carrée (dont nous venons de nous servir) par une trame de triangles équilatéraux.
La forme simplexe pour N dimensions est une forme à N + 1 points.
Donc pour calculer un bruit à l'aide d'un simplexe 2D, il faut 3 points, pour un bruit simplexe 3D, il en faut 4, et pour un bruit 4D, il en faut 5.
en regard des 4 coins du carré, des 8 points du cube et des 16 de l'hypercube, le bénéfice est considérable !
En deux dimensions, l'interpolation se comporte comme dans un bruit normal, en interpolant les valeurs des coins d'un cellule.
A la différence qu'ici, en utilisant une trame de simplexes, nous devons uniquement interpoler entre 3 valeurs.
Comment fabrique-t'on la trame de simplexes?
Encore une trouvaille brillante, la trame de simplexes s'obtient en subdivisant les cellules d'une grille carrée en 2 triangles isocèles puis en leur appliquant un cisaillement (skewing) pour les rendre équilatéraux.
Puis, comme Stefan Gustavson l'explique dans sa publication :
_"... by looking at the integer parts of the transformed coordinates (x,y) for the point we want to evaluate,
we can quickly determine which cell of two simplices that contains the point. By also comparing the magnitudes of x and y,
we can determine whether the point is in the upper or the lower simplex, and traverse the correct three corner points."_
Dans le code suivant, vous pouvez décommenter la ligne 44 pour voir comment la grille est cisaillée (skewed) et la ligne 47 pouvoir comment la trame de simplexes est construite.
Notez comment, ligne 22, nous divisons le carré cisaillé en deux triangles équilatéraux en détectant simplement si
`x > y` (triangle du "bas") ou si `y > x` (triangle du "haut").<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Une autre amélioration apportée par Perlin avec le bruit simplexe est le remplacement de la spline cubique d'Hermite
( _f(x) = 3x^2-2x^3_ , qui est ce que fait un
`smoothstep()`) par une spline Quintique d'Hermite ( _f(x) = 6x^5-15x^4+10x^3_ ).
Ce changement a pour effet de rendre les bords des courbes plus plats et permet une sutture invisible entre cellules.
Pour voir le principe, décommentez la seconde formule de l'exemple suivant (ou regardez les 2 équations côte à côte ici).<div class="simpleFunction" data="
// Cubic Hermine Curve. Same as SmoothStep()
y = xx(3.0-2.0*x);
// Quintic Hermine Curve
//y = xxx(x(x*6.-15.)+10.);
"></div>
Remarquez comme le début et la fin de la courbe changent vers 0 et 1. Vous pouvez en lire plus sur le site de Ken Perlin.
Ces améliorations ont conduit à cette oeuvre d'art algorithmique qu'est le bruit simplexe.
Ci-dessous vous trouverez une implémentation de cet algorithm par Ian McEwan (décrite dans cette publication)
qui sera peut-être un peu difficile d'accès mais qui vous permettra de réaliser qu'au fond c'est moins compliqué qu'il n'y paraît.
Bon... assez parlé technique, il est temps d'utiliser cette ressource de façon créative :
* Observez chaque implémentation du bruit, imaginez que ce sont des matériaux bruts, comme un bloc de marbre pour un sculpteur.
Que pouvez-vous dire de l'impression que chacun "dégage" ? Plissez les yeux pour vous aider, comme quand vous regardez les nuages.
Que voyez vous ? Qu'est ce que ça vous rappelle ? Quelles utilisations potentielles voyez vous pour chaque type de bruit ? Essayez de concrétiser ces idées.
* Créez un shader qui donne l'illusion d'un flux ; une lava-lamp, des goutes d'encre, de l'eau, etc.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Utilisez le bruit simplexe pour texturer un de vos exercices précédents :
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
Dans ce chapitre, nous avons appris à contrôler le chaos et ce n'était pas chose aisée ! Devenir Maître du Bruit demande temps et énergie.
Dans les chapitre suivants, nous verrons d'autres techniques pour perfectionner vos talents et obtenir le maximum de vos fonctions de bruit pour enrichir vos shaders.
D'ici là, prenez le temps de contempler la nature et les motifs qu'elle vous offre.
La capacité d'observation est au moins aussi importante (sinon plus) que la capacité de recréer ces motifs.
Sortez et profitez du reste de votre journée !
<p style="text-align:center; font-style: italic;">"Parler à l'arbre, s'en faire un ami." Bob Ross
</p>
---
11/README Id (11/README-id.md)
Noise
Waktunya istirahat! kita telah bermain dengan fungsi acak yang terlihat seperti derau putih TV, kepala kita masih berputar memikirkan shader, dan mata kita lelah. Waktunya jalan-jalan keluar!
kita merasakan udara di kulit kita, matahari di wajah kita. Dunia adalah tempat yang hidup dan kaya. Warna, tekstur, suara. Saat kita berjalan kita tidak dapat menghindari memperhatikan permukaan jalan, bebatuan, pepohonan dan awan.
Ketidakpastian tekstur ini bisa disebut "acak", tetapi tidak terlihat seperti acak yang kita mainkan sebelumnya. “Dunia nyata” adalah tempat yang kaya dan kompleks! Bagaimana kita bisa memperkirakan variasi ini secara komputasi?
Ini adalah pertanyaan yang coba dipecahkan oleh Ken Perlin pada awal 1980-an ketika dia ditugaskan untuk menghasilkan tekstur yang lebih realistis untuk film "Tron". Menanggapi itu, dia datang dengan algoritma suara pemenang Oscar yang elegan. (Bukan masalah besar.)
Berikut ini bukanlah algoritma derau Perlin klasik, tetapi ini adalah titik awal yang baik untuk memahami cara menghasilkan derau.
<div class="simpleFunction" data="
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i); //rand() is described in the previous chapter
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
Dalam baris ini kita melakukan sesuatu yang mirip dengan apa yang kita lakukan di bab sebelumnya. kita membagi bilangan mengambang kontinu (
`x`) menjadi komponen bilangan bulat (`i`) dan pecahan (`f`). kita menggunakan `floor()` untuk mendapatkan `i` dan `fract()` untuk mendapatkan `f`. Kemudian kita menerapkan `rand()` ke bagian integer dari `x`, yang memberikan nilai acak unik untuk setiap integer.Setelah itu Anda melihat dua baris yang diberi komentar. Yang pertama menginterpolasi setiap nilai acak secara linier.
glsly = mix(rand(i), rand(i + 1.0), f);
Lanjutkan dan hapus komentar pada baris ini untuk melihat tampilannya. kita menggunakan penyimpanan nilai
`fract() ` di f hingga `mix() ` dua nilai acak.Pada poin di buku ini, kita telah belajar bahwa kita bisa melakukan lebih baik daripada interpolasi linier, bukan?
Sekarang coba hapus komentar pada baris berikut, yang menggunakan interpolasi
`smoothstep()` alih-alih yang linier.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Setelah menghapus komentar, perhatikan bagaimana transisi antara puncak menjadi mulus. Dalam beberapa implementasi noise, Anda akan menemukan bahwa programmer lebih suka membuat kode kurva kubik mereka sendiri (seperti rumus berikut) daripada menggunakan
`smoothstep()`.glslfloat u = f f (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation
Keacakan halus ini adalah pengubah permainan untuk insinyur atau seniman grafis - ini memberikan kemampuan untuk menghasilkan gambar dan geometri dengan perasaan organik. Algoritma noise Perlin telah diterapkan berulang kali dalam berbagai bahasa dan dimensi untuk membuat karya yang memukau untuk semua jenis penggunaan kreatif.
Sekarang giliran Anda:
* Buat fungsi
`float noise(float x)` Anda sendiri.* Gunakan fungsi noise Anda untuk menganimasikan bentuk dengan memindahkannya, memutarnya, atau menskalakannya.
* Buat komposisi animasi dari beberapa bentuk 'menari' bersama menggunakan noise.
* Bangun bentuk "yang terlihat organik" menggunakan fungsi noise.
* Setelah Anda memiliki "makhluk" Anda, cobalah untuk mengembangkannya lebih jauh menjadi karakter dengan memberinya gerakan tertentu.
Noise 2D
Sekarang kita tahu bagaimana melakukan noise dalam 1D, sekarang saatnya beralih ke 2D. Dalam 2D, alih-alih melakukan interpolasi antara dua titik garis (
`fract(x)` dan `fract(x)+1.0`), kita akan melakukan interpolasi di antara empat sudut persegi luas bidang (`fract(st)`, `fract(st)+vec2(1.,0.)`, `fract(st)+vec2(0.,1.)` dan `fract(st)+vec2(1.,1.)text/ Detailed source-code truncated for AI context efficiency. /
vec2`), bukan nilai tunggal (`float`). Klik pada gambar berikut untuk melihat kode dan cara kerjanya.Luangkan waktu sejenak untuk melihat kedua contoh oleh Inigo Quilez dan perhatikan perbedaan antara value noise dan noise gradien.
Seperti seorang pelukis yang memahami cara kerja pigmen pada catnya, semakin banyak yang kita ketahui tentang penerapan noise, semakin baik kita dapat menggunakannya. Misalnya, jika kita menggunakan implementasi noise dua dimensi untuk memutar ruang tempat garis lurus dirender, kita dapat menghasilkan efek swirly berikut yang terlihat seperti kayu. Sekali lagi Anda dapat mengklik gambar untuk melihat seperti apa kodenya.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Black splatter
color -= smoothstep(.35,.4,noise(st*10.)); // Holes on splatter
Cara lain untuk mendapatkan pola menarik dari noise adalah dengan memperlakukannya seperti bidang jarak dan menerapkan beberapa trik yang dijelaskan dalam bab Bentuk.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Black splatter
color -= smoothstep(.35,.4,noise(st*10.)); // Holes on splatter
Cara ketiga menggunakan fungsi derau adalah memodulasi bentuk. Ini juga membutuhkan beberapa teknik yang kita pelajari di [bab tentang bentuk] (../ 07 /).
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Untuk Anda berlatih:
* Pola generatif lain apa yang dapat Anda buat? Bagaimana dengan granit? marmer? magma? air? Temukan tiga gambar tekstur yang Anda minati dan terapkan secara algoritme menggunakan noise.
* Gunakan noise untuk memodulasi bentuk.
Bagaimana dengan menggunakan noise untuk gerakan? Kembali ke [bab Matrix] (../08/). Gunakan contoh terjemahan yang menggerakkan "+", dan terapkan beberapa gerakan acak dan gangguan* padanya.
* Buat Jackson Pollock generatif.
Peningkatan Noise
Peningkatan oleh Perlin ke noise non-simpleks aslinya noise Simpleks, adalah penggantian kurva Hermite kubik ( _f(x) = 3x^2-2x^3_, yang identik dengan fungsi
`smoothstep()text/ Detailed source-code truncated for AI context efficiency. /
x > y` (segitiga "bawah ") atau``y > x` (segitiga "atas").<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Semua peningkatan ini menghasilkan mahakarya algoritmik yang dikenal sebagai Simplex Noise. Berikut ini adalah implementasi GLSL dari algoritma ini yang dibuat oleh Ian McEwan dan Stefan Gustavson (dan disajikan dalam [makalah ini) yang terlalu rumit untuk tujuan pendidikan, tetapi Anda akan dengan senang hati mengekliknya dan melihat bahwa itu kurang samar dari yang Anda harapkan, dan kodenya pendek dan cepat.
Yah... cukup teknis, inilah saatnya Anda menggunakan sumber daya ini dengan cara ekspresif Anda sendiri:
* Renungkan bagaimana setiap implementasi noise terlihat. Bayangkan mereka sebagai bahan mentah, seperti batu marmer untuk pematung. Apa yang dapat Anda katakan tentang "perasaan" yang dimiliki masing-masing? Tutup mata Anda untuk memicu imajinasi Anda, seperti saat Anda ingin menemukan bentuk di awan. Apa yang kamu lihat? Anda diingatkan tentang apa? Menurut Anda, setiap implementasi noise dapat dibuat menjadi apa? Ikuti nyali Anda dan cobalah untuk mewujudkannya dalam kode.
* Buat shader yang memproyeksikan ilusi aliran. Seperti lampu lava, tetesan tinta, air, dll.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Gunakan Simplex Noise untuk menambahkan tekstur pada karya yang telah Anda buat.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
Dalam bab ini kita telah memperkenalkan beberapa kendali atas kekacauan. Itu bukanlah pekerjaan yang mudah! Menjadi ahli penyok noise membutuhkan waktu dan usaha.
Dalam bab-bab berikut, kita akan melihat beberapa teknik terkenal untuk menyempurnakan keterampilan Anda dan memaksimalkan noise Anda untuk merancang konten generatif berkualitas dengan shader. Sampai saat itu nikmati waktu di luar sambil merenungkan alam dan polanya yang rumit. Kemampuan Anda untuk mengamati membutuhkan dedikasi yang sama (atau mungkin lebih) daripada keterampilan membuat Anda. Pergilah keluar dan nikmati sisa hari ini!
<p style = "text-align: center; font-style: italic;"> "Bicaralah dengan pohon, bertemanlah dengannya." Bob Ross
</p>
---
11/README It (11/README-it.md)
Rumore
È tempo di fare una pausa! Abbiamo giocato con le funzioni random che sembrano creare del rumore bianco di una televisione, la testa ci gira ancora solo al pensiero degli shader e i nostri occhi sono stanchi. È ora di andare a fare una passeggiata!
Sentiamo l'aria sulla nostra pelle, il sole in faccia. Il mondo è un posto così vivido e ricco: colori, texture, suoni. Mentre camminiamo, non possiamo evitare di notare la superficie delle strade, rocce, alberi e nuvole.
L'imprevedibilità di queste texture potrebbe essere intesa come "random", ma non sembrano di certo il risultato delle funzioni random che abbiamo sperimentato in precedenza. Il "mondo reale" è un posto così ricco e complesso! Come possiamo approssimare questa varietà computazionale?
Questa è stata la domanda che Ken Perlin stava cercando di risolvere nei prima anni '80 quando gli fu commissionato di generare delle texture più realistiche per il film "Tron". Il risultato delle sue ricerche fu un elegante algoritmo di rumore vincitore del premio Oscar. (Non disperatevi.)
Quanto segue non è il classico algoritmo di Perlin, ma è un buon punto di partenza per capire come generare del rumore.
<div class="simpleFunction" data="
float i = floor(x); // intero
float f = fract(x); // frazione
y = rand(i); //rand() è spiegata nel capitolo precedente
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
In queste righe stiamo facendo qualcosa di simile a quello che abbiamo fatto nel capitolo precedente. Stiamo suddividendo un numero floating continuo (
`x`) nel suo intero (`i`) e nelle componenti frazionarie (`f`). Usiamo `floor()` per ottenere `i` e `fract()` per ottenere `f`. Poi applichiamo `rand()` per la parte intera di `x`, che dà un valore random unico a ogni integer.Dopo di che si osservino le due righe commentate. La prima interpola ogni valore random in modo lineare.
glsly = mix(rand(i), rand(i + 1.0), f);
Andate avanti e rimuovete il commento di questa linea per vedere che cosa succede. Utilizziamo la parte frazionale
f per mischiare (`mix()`) i due valori random.A questo punto del libro, abbiamo imparato che possiamo fare meglio d'una interpolazione lineare, giusto?
Ora provate a decommentare la riga seguente, che utilizza una interpolazione
`smoothstep()` invece di una lineare.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Tolto il commento, si noti che la transizione tra i picchi diventa armoniosa. In alcune implementazioni del rumore, troverete che i programmatori preferiscono creare le proprie curve cubiche (come la seguente formula) invece di utilizzare la funzione
`smoothstep()`.glslfloat u = f f (3.0 - 2.0 * f ); // curva cubica personalizzata
y = mix(rand(i), rand(i + 1.0), u); // e con un'interpolazione
Questo casualità armoniosa è un punto di svolta per gli ingegneri grafici o artisti perché fornisce la capacità di generare delle immagini e delle geometrie con un tocco organico. L'Algoritmo del Rumore di Perlin è stato implementato più e più volte in diversi linguaggi e spazio dimensionale per ottenere ogni tipo di uso creativo.
Ora è il vostro turno:
* Create la vostra funzione
`float noise(float x)`.* Utilizzate la funzione di rumore per animare una forma geometrica spostandola, ruotandola o ridimensionandola.
* Fate 'ballare' diverse forme geometriche usando il rumore.
* Costruite forme "organiche" utilizzando la funzione di rumore.
* Una volta che avete la vostra "creatura", cercate di svilupparla ulteriormente assegnandogli un particolare movimento.
Rumore 2D
Ora che sappiamo come creare il rumore in una dimensione, è il momento di passare alla 2D. Nelle due dimensioni, invece di interpolare tra due punti di una linea (
`fract(x)` e `fract(x)+1.0`), interpoleremo tra i quattro angoli di un piano quadrato (`fract(st)`, `fract(st)+vec2(1.,0.)`, `fract(st)+vec2(0.,1.)` e `fract(st)+vec2(1.,1.)`).Allo stesso modo, se si vuole ottenere del rumore 3D dobbiamo interpolare tra gli otto angoli di un cubo. Questa tecnica consiste nell'interpolazione di valori random, che è il motivo per cui si chiama rumore del valore (value noise) .
Come per l'esempio 1D, questa interpolazione non è lineare, ma cubica, e interpola senza problemi tutti i punti all'interno di una griglia quadrata.
Date un'occhiata alla seguente funzione di rumore.
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Iniziamo scalando lo spazio di 5 (linea 45) per vedere l'interpolazione tra i quadrati della griglia. Poi all'interno della funzione rumore suddividiamo lo spazio in celle. Memorizziamo la posizione "integer" della cella insieme alle posizioni frazionarie all'interno della cella. Usiamo la posizione dell'integer per calcolare le coordinate dei quattro angoli e ottenere un valore random per ciascuno di essi (linee 23-26). Infine, alla linea 35 interpoliamo tra i 4 valori random degli angoli utilizzando le posizioni frazionarie che abbiamo ricavato in precedenza.
Ora è il vostro turno. Provate i seguenti esercizi:
* Cambiate il moltiplicatore alla linea 45. Provate ad animarlo.
* A quale livello di zoom il rumore comincia a sembrare di nuovo random?
* A che livello di zoom il rumore è impercettibile?
* Provate a collegare questa funzione rumore alle coordinate del mouse.
* Che cosa succede se utilizziamo il gradiente del rumore come un campo di distanza? Fatene qualcosa di interessante.
* Ora che avete raggiunto un certo controllo sull'ordine e il caos, è il momento di utilizzare tale conoscenza. Effettuate una composizione di rettangoli, colori e rumore che assomiglia a un dipinto di Mark Rothko.
Usare il Rumore nella Progettazione Generativa
Gli algoritmi di rumore sono stati originariamente progettati per dare un naturale je ne sais quoi alle texture digitali. Le implementazioni 1D e 2D che abbiamo visto fino ad ora erano interpolazioni tra valori random, che è il motivo per cui sono chiamate valore di rumore, ma ci sono altri modi per ottenere il rumore...
Come avete scoperto negli esercizi precedenti, il valore del rumore tende a sembrare a dei "blocchi". Per ridurre questo effetto, nel 1985 Ken Perlin ha sviluppato un'altra implementazione dell'algoritmo denominato Rumore di Gradiente (Gradient Noise). Ken aveva capito come interpolare dei gradienti random invece che dei valori. Questi gradienti sono stati il risultato d'una funzione random 2D che restituisce le direzioni (rappresentata da un
`vec2`) al posto di singoli valori (`float`). Clicca sull'immagine seguente per vedere il codice e come funziona.Guardate questi due esempi di Inigo Quilez e prestate attenzione alle differenze tra il valore del rumore e il gradiente del rumore.
Come un pittore che capisce come i pigmenti funzionano, più sappiamo sulle varie implementazioni del rumore e meglio saremo in grado di usarle. Ad esempio, se si usa un'implementazione del rumore in due dimensioni per ruotare lo spazio in cui sono visualizzate delle linee rette, otterremo il seguente effetto visuale che sembra del legno. Anche in questo caso è possibile fare clic sull'immagine per vedere il codice.
glslpos = rotate2d( noise(pos) ) * pos; // ruota lo spazio
pattern = lines(pos,.5); // disegna delle linee
Un altro modo per ottenere dal rumore dei pattern interessanti è quello di trattarlo come un campo di distanza e applicare alcuni dei trucchi descritti nel capitolo sulle Figure.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // schizzo nero
color -= smoothstep(.35,.4,noise(st*10.)); // buchi sugli schizzi
Un terzo modo di utilizzare la funzione di rumore è di modulare una forma. Questo richiede anche alcune delle tecniche che abbiamo imparato nel capitolo sulle figure.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Per la vostra pratica:
* Quali altri pattern generativi si possono fare? Che dire del granito? marmo? magma? acqua? Trovate tre immagini di texture di vostro interesse e come poterle realizzare algoritmicamente usando il rumore.
* Utilizzate il rumore per modulare una forma.
Cosa succede se usate il rumore per il movimento? Tornate al capitolo sulle Matrici. Utilizzate l'esempio per traslare il "+", e applicateci alcuni movimenti random e derivati dal rumore*.
* Fate un dipinto generativo alla Jackson Pollock.
Rumore Simplesso
Per Ken Perlin il successo del suo algoritmo non è stato sufficiente. Ha pensato che avrebbe potuto funzionare meglio. Al Siggraph 2001 ha presentato il "rumore simplesso", in cui ha ottenuto i seguenti miglioramenti rispetto al precedente algoritmo:
* Un algoritmo con una minore complessità computazionale e un minor numero di moltiplicazioni.
* Un rumore in grado di scalare a dimensioni superiori con meno costo computazionale.
* Un rumore senza artefatti direzionali.
* Un rumore con gradienti ben definiti e continui che possono essere calcolati abbastanza facilmente.
* Un algoritmo che è facile da implementare sull'hardware.
So cosa state pensando... "Chi è costui?" Sì, il suo lavoro è fantastico! Ma sul serio, come ha fatto a migliorare l'algoritmo? Beh, abbiamo visto come per le due dimensioni ha interpolato 4 punti (angoli di un quadrato); allo stesso modo possiamo indovinare che per tre (vedi un'implementazione qui) e quattro dimensioni abbiamo bisogno di interpolare rispettivamente 8 e 16 punti. Giusto? In altre parole per N dimensioni è necessario interpolare agevolmente dai 2 agli N punti(2^N). Ma Ken ha elegantemente notato che, anche se il quadrato è la scelta ovvia per riempire lo spazio, la figura più semplice in 2D è il triangolo equilatero. Così ha iniziato a sostituire la griglia quadrata (abbiamo appena appreso come usarla) per una griglia simplessa di triangoli equilateri.
La figura simplessa per N dimensioni è una figura con N + 1 angoli. In altre parole, un angolo in meno da calcolare in 2D, 4 in meno in 3D e 11 in meno in 4D! Questo è un enorme miglioramento!
In due dimensioni l'interpolazione avviene in modo simile al rumore regolare, interpolando i valori degli angoli di una sezione. Ma in questo caso, utilizzando una griglia simplessa, abbiamo solo bisogno di interpolare la somma di 3 angoli.
Come è fatta la griglia simplessa? Con un'altra mossa brillante ed elegante, la griglia simplessa può essere ottenuta suddividendo le celle di una regolare griglia a 4 angoli in due triangoli isosceli e poi distorcendola finché ogni triangolo sia equilatero.
Poi, come Stefan Gustavson descrive in questo articolo: _".. cercando le parti intere delle coordinate trasformate (x,y) per il punto che vogliamo valutare, siamo in grado di determinare rapidamente quale cella di due simplessi contiene il punto. Confrontando anche le grandezze di x e y, siamo in grado di determinare se il punto è nel simplesso più alto o più basso, e percorrere i tre punti d'angolo corretti."_
Nel seguente codice è possibile rimuovere il commento alla linea 44 per vedere come la griglia è distorta, e poi rimuovere il commento alla linea 47 per vedere come una griglia simplessa può essere costruita. Notate come alla linea 22 stiamo suddividendo il quadrato distorto in due triangoli equilateri semplicemente controllando se
`x > y` (triangolo "inferiore") oppure `y > x` (triangolo "superiore").<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Un altro miglioramento introdotto da Perlin con il Rumore Simplesso, è la sostituzione della Curva Cubica di Hermite ( _f(x) = 3x^2-2x^3_ , che è identica alla funzione
`smoothstep()` ) con una Curva Quintica di Hermite ( _f(x) = 6x^5-15x^4+10x^3_ ). In questo modo entrambe le estremità della curva sono più "piatte" così che ogni limite si unisce con grazia con quello successivo. In altre parole si ottiene una transizione più continua tra le celle. Si può osservare ciò decommentando la seconda formula nel seguente esempio grafico (o osservando le due equazioni fianco a fianco cliccando qui).<div class="simpleFunction" data="
// Curva Cubica di Hermite. Identica alla funzione SmoothStep()
y = xx(3.0-2.0*x);
// Curva Quintica di Hermite
//y = xxx(x(x*6.-15.)+10.);
"></div>
Si noti come le estremità della curva cambino. Si può leggere di più a proposito sul sito di Ken.
Tutti questi miglioramenti si traducono in un capolavoro algoritmico noto come Rumore Simplesso. La seguente è una implementazione GLSL di questo algoritmo fatta da Ian McEwan(e presentato in questo articolo) che è troppo complicata per scopi didattici, ma sarete felici di fare un clic su di esso e vedere che è meno criptica di quanto si possa pensare.
Beh... direi che abbiamo avuto abbastanza tecnicismi, è il momento d'utilizzare queste risorse in modo creativo:
* Osservate ogni implementazione del rumore. Immaginatele come delle materie grezze, come delle rocce di marmo per uno scultore. Che cosa si può dire a proposito delle "sensazione" che ci trasmettono? Chiudete gli occhi per far scattare la vostra immaginazione, come quando si cercano delle forme in una nuvola. Cosa vedi? Cosa ricorderai? Che cosa si potrebbe creare da ogni implementazione del rumore? Seguite le vostre ispirazioni e cercate di realizzarle con il codice.
* Fate uno shader che crea l'illusione di un flusso. Come una lampada di lava, gocce d'inchiostro, dell'acqua, ecc.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Utilizzate il rumore simplesso per aggiungere delle texture a dei lavori precedenti.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
In questo capitolo abbiamo preso controllo sul caos. Non è stato un lavoro facile! Diventare un maestro del rumore richiede tempo e impegno.
Nei capitoli seguenti vedremo alcune tecniche ben note per perfezionare le proprie competenze e ottenere di più dal vostro rumore per progettare del contenuto generativo di qualità. Fino ad allora godetevi un po' di tempo all'aria aperta, contemplate la natura e i suoi pattern complicati. La capacità d'osservazione è altrettanto importante (se non di più) rispetto a quella di creare dei pattern. Andate fuori e godetevi il resto della giornata!
<p style="text-align:center; font-style: italic;">"Parla con l'albero, per farsi un amico." Bob Ross
</p>
---
11/README Pl (11/README-pl.md)
Noise (pol. "szum")
Czas na przerwę! Bawiliśmy się losowymi funkcjami, które wyglądają jak szum telewizyjny (tzw. "szum biały", ang. "white noise"), w głowie wciąż się kręci od myślenia o shaderach, a oczy są po prostu zmęczone. Czas wyjść na spacer!
Czujemy powietrze na skórze, słońce na twarzy. Świat jest tak żywym i bogatym miejscem. Kolory, tekstury, dźwięki. Podczas spaceru widzimy powierzchnię dróg, skał, drzew i chmur.
Nieprzewidywalność tych tekstur można by nazwać "losową", ale nie przypominają one losowości, z którą bawiliśmy się wcześniej. "Prawdziwy świat" jest tak bogatym i złożonym miejscem! Jak możemy zamodelować tę różnorodność obliczeniowo?
To było pytanie, które Ken Perlin próbował rozwiązać we wczesnych latach 80-tych, kiedy otrzymał zlecenie wygenerowania bardziej realistycznych tekstur do filmu "Tron". W odpowiedzi na to wymyślił elegancki algorytm szumu, za który później otrzymał Oskara.
Poniższy kod nie jest klasycznym algorytmem szumu Perlina, ale jest dobrym punktem wyjścia do zrozumienia sposobu generowania szumu.
<div class="simpleFunction" data="
float i = floor(x); // cz. całkowita
float f = fract(x); // cz. ułamkowa
y = rand(i); //funkcja rand() opisana jest w poprzednim rozdziale
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
W tych liniach robimy coś podobnego do tego, co robiliśmy w poprzednim rozdziale. Dzielimy ciągłą liczbę zmiennoprzecinkową (
x) na jej składowe całkowitą (i) i ułamkową (f). Używamy floor() aby uzyskać i oraz fract() aby uzyskać f. Następnie stosujemy rand() do części całkowitej x, co daje unikalną wartość losową dla każdej liczby całkowitej.Spójrz na dwie skomentowane linie. Pierwsza z nich interpoluje liniowo każdą wartość losową.
glsly = mix(rand(i), rand(i + 1.0), f);
Odkomentuj tę linię, aby zobaczyć jak to wygląda. Używamy
f do interpolacji liniowej dwóch sąsiadujących wartości losowych za pomocą funkcji mix().Nauczyliśmy się, że możemy zrobić coś lepszego niż interpolacja liniowa, prawda?
Spróbuj teraz odkomentować kolejną drugą linię, która używa interpolacji
smoothstep() zamiast liniowej.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Po odkomentowaniu zauważ, jak przejście między szczytami staje się gładkie. W niektórych implementacjach szumu można zauważyć, że programiści wolą kodować własne krzywe sześcienne (ang. "cubic curves") (jak w kodzie poniżej) zamiast używać
smoothstep().glslfloat u = f f (3.0 - 2.0 * f ); // spersonalizowana funkcja sześcienna
y = mix(rand(i), rand(i + 1.0), u); // interpolacja z jej pomocą
Ta płynna losowość jest przełomem dla programistów grafiki i artystów - daje możliwość generowania organicznych obrazów i geometrii. Algorytm Szumu Perlina był wielokrotnie implementowany w różnych językach i wymiarach, aby móc tworzyć hipnotyzujące dzieła w celach kreatywnych.
Teraz twoja kolej:
* Stwórz własną funkcję
float noise(float x).* Użyj swojej funkcji szumu do animowania kształtu poprzez przesuwanie go, obracanie lub skalowanie.
* Zrób animowaną kompozycję kilku kształtów "tańczących" razem przy użyciu szumu.
* Skonstruuj "organicznie wyglądające" kształty używając funkcji noise.
Szum 2D
Teraz, gdy wiemy jak zrobić szum w 1D, czas przejść do 2D. W 2D zamiast interpolować między dwoma punktami linii (
rand(x) i rand(x)+1.0), będziemy interpolować pomiędzy czterema narożnikami kwadratowego obszaru płaszczyzny (rand(st), rand(st)+vec2(1.,0.), rand(st)+vec2(0.,1.) oraz rand(st)+vec2(1.,1.)).Podobnie, jeśli chcemy uzyskać szum 3D musimy interpolować pomiędzy ośmioma rogami sześcianu. W tej technice chodzi o interpolację losowych wartości ang. (ang. "random values"), dlatego nazywa się ją value noise.
Podobnie jak w przykładzie 1D, interpolacja ta nie jest liniowa, ale sześcienna, więc płynnie interpoluje wszelkie punkty wewnątrz naszego kwadratowego obszaru.
Przyjrzyj się następującej funkcji szumu.
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Zaczynamy od przeskalowania przestrzeni o 5 (linia 45). Następnie wewnątrz funkcji szumu dzielimy przestrzeń na kafellki. Przechowujemy pozycję kafelka jako cześć całkowitą oraz pozycje wewnątrz kafelka jako część ułamkową. Używamy części całkowitej do obliczenia współrzędnych czterech narożników, otrzymując losową wartość dla każdego z nich (linie 23-26). Na koniec, w linii 35 interpolujemy pomiędzy 4 losowymi wartościami narożników używając części ułamkowej.
Teraz twoja kolej. Spróbuj wykonać następujące ćwiczenia:
* Zmień mnożnik w linii 45. Spróbuj go zanimować.
* Przy jakim poziomie powiększenia szum zaczyna znowu wyglądać kompletnie losowo (jak, wspomniany na początku rozdziału, biały szum)?
* Przy jakim poziomie powiększenia szum jest niezauważalny?
* Spróbuj podpiąć tę funkcję szumu do współrzędnych myszy.
* A gdyby tak potraktować gradient szumu jako pole odległości? Zrób z tym coś ciekawego.
* Teraz, gdy osiągnąłeś już pewną kontrolę nad porządkiem i chaosem, czas wykorzystać tę wiedzę. Stwórz kompozycję z prostokątów, kolorów i szumu, która przypomina nieco złożoność obrazu Marka Rothko.
Szum a design generatywny
Algorytmy szumu zostały pierwotnie zaprojektowane w celu nadania naturalnego je ne sais quoi cyfrowym teksturom. Implementacje 1D i 2D, które widzieliśmy do tej pory, były interpolacjami pomiędzy losowymi wartościami, dlatego nazywane są Value Noise, ale istnieje więcej sposobów na uzyskanie szumu...
Jak odkryłeś w poprzednich ćwiczeniach, value noise ma tendencję do wyglądania "blokowo". Aby zmniejszyć ten blokowy efekt, w 1985 roku Ken Perlin opracował inną implementację algorytmu o nazwie Gradient Noise. Ken wymyślił jak interpolować losowe gradienty zamiast wartości. Gradienty te były wynikiem funkcji losowej 2D, która zwraca kierunki (reprezentowane przez
vec2) zamiast pojedynczych wartości (float). Kliknij na poniższy obrazek, aby zobaczyć kod i sposób jego działania.Poświęć chwilę na przyjrzenie się tym dwóm przykładom autorstwa Inigo Quilez i zwróć uwagę na różnice pomiędzy value noise a gradient noise.
Podobnie jak malarz, który rozumie, jak działają pigmenty jego farb, im więcej wiemy o implementacjach szumu, tym lepiej będziemy mogli z nich korzystać. Na przykład, jeśli użyjemy dwuwymiarowej implementacji szumu do obrócenia przestrzeni, w której renderowane są linie proste, możemy uzyskać następujący drewno-podobny efekt. Ponownie możesz kliknąć na obrazek, aby zobaczyć, jak wygląda kod.
glslpos = rotate2d( noise(pos) ) * pos; // obracanie przestrzeni
pattern = lines(pos,.5); // rysowanie linii
Innym sposobem na uzyskanie ciekawych wzorów z szumu jest potraktowanie go jak pola odległości i zastosowanie niektórych sztuczek opisanych w rozdziale Kształty.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Czarny rozprysk
color -= smoothstep(.35,.4,noise(st*10.)); // Dziury w rozprysku
Trzecim sposobem wykorzystania funkcji szumu jest modulowanie kształtu. To również wymaga pewnych technik, które poznaliśmy w rozdziale o kształtach.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Do poćwiczenia:
* Jaki inny wzór generatywny możesz stworzyć? Co z granitem? marmurem? magmą? wodą? Znajdź trzy zdjęcia interesujących Cię tekstur i zaimplementuj je algorytmicznie za pomocą szumu.
* Użyj szumu do modulacji kształtu.
A co z wykorzystaniem szumu do ruchu? Wróć do rozdziału Macierze. Użyj przykładu z translacją kształtu "+" i zastosuj do niego kilka losowych i szumowych* ruchów.
* Zrób generatywnego Jacksona Pollocka.
Lepszy szum
Ulepszenie oryginalnego szumu Perlina, zwane Simplex Noise, polega na zastąpieniu sześciennej krzywej Hermite'a ( _f(x) = 3x^2-2x^3_ , która jest identyczna z funkcją
smoothstep()) kwintową krzywą interpolacyjną ( _f(x) = 6x^5-15x^4+10x^3_ ). Dzięki temu oba końce krzywej są bardziej "płaskie", więc każda granica z wdziękiem zszywa się z następną. Innymi słowy, otrzymujesz bardziej ciągłe przejście między komórkami. Możesz to zobaczyć, odkomentowując drugą formułę w poniższym przykładzie wykresu (lub zobacz dwa równania obok siebie tutaj).<div class="simpleFunction" data="
// Cubic Hermite Curve. Same as SmoothStep()
y = xx(3.0-2.0*x);
// Quintic interpolation curve
//y = xxx(x(x*6.-15.)+10.);
"></div>
Zauważ, jak zmieniają się końce krzywej. Więcej na ten temat możesz usłyszeć z ust Kena Perlina.
Simplex Noise
Dla Kena Perlina sukces jego algorytmu nie był wystarczający. Uważał, że może on działać lepiej. Na Siggraph 2001 zaprezentował "simplex noise", w którym osiągnął następujące ulepszenia w stosunku do poprzedniego algorytmu:
* Algorytm o mniejszej złożoności obliczeniowej i mniejszej liczbie mnożeń.
* Szum, który skaluje się do wyższych wymiarów przy mniejszym koszcie obliczeniowym.
* Szum bez artefaktów kierunkowych.
* Szum z dobrze zdefiniowanymi i ciągłymi gradientami, o niskim koszcie obliczeniowym.
* Algorytm, który jest łatwy do zaimplementowania w hardware'rze.
Wiem, co myślisz... "Kim jest ten człowiek?" Tak, jego praca jest fantastyczna! Ale poważnie, w jaki sposób ulepszył ten algorytm? Cóż, widzieliśmy jak dla dwóch wymiarów interpolował 4 punkty (rogi kwadratu); możemy więc poprawnie zgadnąć, że dla trzech (zobacz implementację tutaj) i czterech wymiarów musimy interpolować 8 i 16 punktów. Prawda? Innymi słowy dla N wymiarów musisz płynnie interpolować 2 do N punktów (2^N). Ale Ken sprytnie zauważył, że chociaż oczywistym wyborem dla kształtu wypełniającego przestrzeń jest kwadrat, najprostszym kształtem w 2D jest trójkąt równoboczny. Zaczął więc od zastąpienia siatki kwadratowej (niedawno nauczyliśmy się jej używać) siatką trójkątów równobocznych (inaczej zwaną siatką sympleksową).
Kształt dla N wymiarów to kształt z N + 1 wierzchołkami. Innymi słowy jeden wierzchołek mniej do obliczenia w 2D, 4 wierzchołki mniej w 3D i 11 wierzchołków mniej w 4D! To ogromna poprawa!
W dwóch wymiarach interpolacja odbywa się podobnie do zwykłego szumu, poprzez interpolację wartości wierzchołków odcinka. Ale w tym przypadku, dzięki zastosowaniu siatki sympleksowej, musimy tylko interpolować sumę 3 wierzchołków.
Jak powstaje siatka sympleksowa? W kolejnym błyskotliwym i eleganckim posunięciu, można ją uzyskać poprzez podział kwadratowych kafelków na dwa trójkąty równoramienne, a następnie przekrzywienia ich, aż każdy trójkąt będzie równoboczny. Proces ten szerzej opisany jest w artykule Stefana Gustavsona.
W poniższym kodzie możesz odkomentować linię 44, aby zobaczyć jak siatka jest przekrzywiona, a następnie odkomentować linię 47, aby zobaczyć siatkę simpleksową. Zauważ jak w linii 22 dzielimy przekrzywiony kwadrat na dwa trójkąty równoboczne poprzez wykrycie czy
x > y (dolny" trójkąt) lub y > x (górny" trójkąt).<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Wszystkie te ulepszenia skutkują algorytmicznym arcydziełem, jakim jest Simplex Noise. Poniżej znajduje się implementacja GLSL tego algorytmu wykonana przez Iana McEwana i Stefana Gustavsona (i przedstawiona w tym artykule), która w celach edukacyjnych jest nadmiernie skomplikowana , ale przekonasz się, że jest też mniej enigmatyczna niż można by się spodziewać, a kod jest krótki i szybki.
Cóż... dość technicznych rozważań, czas na wykorzystanie tego narzędzia we własny, ekspresyjny sposób:
* Kontempluj, jak wygląda każda implementacja szumu. Wyobraź sobie je jako surowy materiał, jak marmurowy kamień dla rzeźbiarza. Co możesz powiedzieć o "uczuciu", jakie ma każda z nich? Zmruż oczy, aby uruchomić wyobraźnię, tak jak wtedy, gdy chcesz znaleźć kształty w chmurze. Co widzisz? Co ci się przypomina? W co każda implementacja szumu mogłaby zostać przeobrażana? Podążając za swoją intuicją, spróbuj zrealizować to w kodzie.
* Zrób shader, który tworzy iluzję przepływu. Jak lampa lawowa, krople atramentu, woda itp.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Użyj Simplex Noise, aby dodać trochę tekstury do pracy, którą już wykonałeś.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
W tym rozdziale wprowadziliśmy pewną kontrolę nad chaosem. Nie była to łatwa praca! Zostanie zaklinaczem chaosu wymaga czasu i wysiłku.
W następnych rozdziałach zobaczymy kilka dobrze znanych technik, które pozwolą ci udoskonalić swoje umiejętności i wydobyć więcej z szumu, aby zaprojektować wysokiej jakości generatywne dzieła za pomocą shaderów. Do tego czasu ciesz się czasem na zewnątrz, kontemplując naturę i jej zawiłe wzory. Twoja umiejętność obserwacji wymaga równego (a może nawet większego) poświęcenia niż twoje umiejętności tworzenia. Wyjdź na zewnątrz i ciesz się resztą dnia!
<p style="text-align:center; font-style: italic;">"Talk to the tree, make friends with it." Bob Ross
</p>
---
11/README Pt (11/README-pt.md)
Noise (Ruído)
Chegou a hora de um intervalo! Nós estamos explorando todas essas funções de aleatório que se parecem com ruído branco de TV, nossa cabeça ainda está rodando pensando em shaders, nossos olhos estão cansados. Hora de dar uma volta!
Nós sentimos o ar em nossa pele, o sol em nosso rosto. O mundo é um lugar vívido e rico. Cores, texturas, sons. Enquanto nós caminhamos, nós não conseguimos evitar perceber a superfície das estradas, das pedras, árvores e nuvens.
A imprevisibilidade dessas texturas podem ser chamadas de "aleatório", mas elas não se parecem com o mesmo aleatório que estávamos explorando antes. O "mundo real" é um lugar tão rico e complexo! Como podemos aproximar computacionalmente essa variedade.
Essa é o mistério que Ken Perlin tentava resolver nos começo dos anos 80, quando ele foi contratado para gerar texturas mais realistas para o filme "Tron". Em resposta a isso, ele criou um elegante algoritmo Vencedor de Oscar de Noise (Ruído).
O que vem a seguir não é o algoritmo clássico de Perlin Noise, mas é um bom ponto de partida para entender como gerar ruído.
<div class="simpleFunction" data="
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i); //rand() is described in the previous chapter
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
Nessas linhas, nós estamos fazendo algo similar com o que nós fizemos no capítulo anterior. Estamos subdividindo um número flutuante contínuo (
`x`) nos componentes: inteiro (`i`) e um fracionária (`f`). Nós usamos `floor` para obter `f`. Então nós aplicamos `rand()` na parte inteira de `x`, que nos retorna um valor aleatório único para cada inteiro.Após isso, você vê duas linhas comentadas. A primeira interpola cada valor aleatório de forma linear.
glsly = mix(rand(i), rand(i + 1.0), f);
Vá em frente e descomente estas linhas para ver o resultado. Nós usamos o valor
`fract()` armazenado em f para `mix()` (interpolar) os dois valores aleatórios.A esta altura do livro, nós já aprendemos que podemos fazemos melhor que apenas uma interpolação linear, certo?
Agora, experimente descomentar a linha seguinte, que usa uma interpolação de
`smoothstep()` em vez de uma linear.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Depois de descomentá-la, perceba como a transição entre os picos se suaviza. Em algumas implementações de noise, você perceberá que os programadores preferem escrever as próprias curvas cúbicas (como esta na fórmula seguinte) em vez de usar
`smoothstep()`.glslfloat u = f f (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation
Esta suavização alatória é um divisor de águas para engenheiros gráficos ou artistas - ela nos proporciona a habilidade de gerar imagens e geometria com um sentimento orgânico. O algoritmo de Perlin Noise foi implementado repetidas vezes em diversas linguagens e dimensões para criar peças hipnotizantes para todos os tipos de uso criativo.
Agora é a sua vez:
* Faça sua própria função
`float noise(float x)`.* Use sua função de Noise para animar uma forma se movendo, rotacioná-la ou alterar sua escala.
* Faça uma composição animada com múltiplas formas 'dançando' juntas utilizando noise.
* Construa uma forma de aparência "orgânica" utilizando a função de noise.
* Depois de conceber sua "criatura", tente ir além em transformá-la em um personagem ao atribuí-la um movimento próprio.
## Noise 2D
Agora que nós sabemos como fazer Noise em 1D, está na hora de partirmos para 2D. Em 2D, em vez de interpolar entre dois pontos de uma linha (
`fract(x)` e `fract(x)+1.0`), nós vamos interpolar entre quatro vértices de uma área quadrada de um plano (`fract(st)`, `fract(st)+vec2(1.,0.)`, `fract(st)+vec2(0.,1.)` e `fract(st)+vec2(1.,1.)`).
Analogicamente, se nós quisermos obter um ruído 3D, nós precisamos interpolá-lo entre os oito vértices do cubo. Esta técnica é tudo uma questão de interpolar valores, por isso é chamada Ruído de Valor.
Como no exemplo 1D, esta interpolação não é linear, mas cúbica, na qual suavemente se interpola qualquer ponto dentro de nossa grade quadrada.
Dê uma olhada na seguinte função noise.
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Nós começamos alterando a escala do espaço por 5 para que se possa ver a interpolação entre os quadrados da grade. Então, dentro da função noise, nós subdividimos o espaço em células. Nós armazenamos a posição inteira ao longo da célula com a posição fracionária de dentro da célula. Nós usamos a posição inteira para calcular as quatro coordenadas dos cantos e obtemos um valor aleatório para cada uma (linhas 23-26). Por último, na linha 35, interpolamos entre os 4 valores aleatórios dos cantos usando a posição fracionária que armazenamos antes.
Agora é a sua vez. Tente os seguintes exercícios:
* Mude o multiplicador na linha 45. Tente animá-lo.
* Em qual nível de zoom o Noise começa a se parecer como Random novamente?
* Em qual nível de zoom o Noise é imperceptível?
* Tente associar a função de noise com as coordenadas do mouse.
* Agora, e se nós tratarmos o gradiente do Noise como um campo de distância? Faça algo interessante a partir disso.
* Agora que conseguimos obter controle sobre a ordem e o caos, é hora de usar nosso conhecimento. Faça uma composição com retângulos, cores e ruído que nos lembre um pouco da complexidade de uma pintura de Mark Rothko.
Utilizando Noise em Designs Generativos
Algoritmos de noise foram originalmente desenhados para nos retornar um natural je ne sais quoi em texturas digitais. As implementações 1D e 2D que nós vimos até agora são interpolações entre valores aleatórios, por essa razão são chamados Ruído de Valor, mas existem muitas outras formas de obter ruído...
Como você percebeu nos últimos exercícios, valores ruidosos tendem a parecer "quadriculados". Para diminuir esse efeito, em 1985 Ken Perlin desenvolveu outra implementação do algoritmo chamado Ruído de Gradiente. Ken descobriu como interpolar gradientes aleatórios ao invés de valores. Estes gradientes são o resultado de uma função aleatória 2D que retorna direções (representadas por um
`vec2`) em vez de valores únicos (`float`). Clique na seguinte imagem para ver o código e como essa técnica funciona.Tome um minuto para olhar estes dois exemplos por Inigo Quilez e preste atenção para as diferenças entre value noise e gradient noise.
Como um pintor que entende como os pigmentos de suas pinturas funcionam, quanto mais sabermos sobre as implementações de noise, melhor nós poderemos usá-las. Por exemplo, se nós usarmos uma implementação de noise bidimensional para rotacionar o espaço onde linhas retas são renderizadas, nós podemos reproduzir o seguinte efeito de redemoinho que nos lembra madeira. Você pode clicar na imagem para ver como esse código se parece.
glslpos = rotate2d( noise(pos) ) * pos; // rotate the space
pattern = lines(pos,.5); // draw lines
Um outro jeito de se interessar por padrões de ruído, é tratá-los como um campo de distância e aplicar alguns truques descritos no capítulo Formas.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Black splatter
color -= smoothstep(.35,.4,noise(st*10.)); // Holes on splatter
Uma terceira forma é usar as funções de noise para modular uma forma. Essa forma também pode requerer algumas técnicas que aprendemos no capítulo sobre formas.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Para você praticar:
* Quais outros padrões generativos você pode fazer? Que tal granito? Mármore? Magma? Água? Encontre três fotos de textura que você tem interesse em implementá-los algoritmicamente usando noise.
* Use noise para modular uma forma.
E quanto a usar noise para movimento? Volte ao capítulo Matrizes. Use o exemplo de translação que circunda o "+" e aplique um pouco de movimentos random e noise* e ele.
* Faça um generativo Jackson Pollock.
Melhorando o noise
Uma melhoria por Perlin em seu noise non-simplex original Simplex Noise, é a substituição da curva cúbica Hermite ( _f(x) = 3x^2-2x^3_ , que é idêntica à função
`smoothstep()`) com uma interpolação da curva quíntica ( _f(x) = 6x^5-15x^4+10x^3_ ). Isso faz com que ambos finais da curva sejam mais "achatados" então cada borda graciosamente se costurem com a próxima. Em outras palavras, você obtém uma transição mais contínua entre as células. Você pode observar isso ao descomentar a segunda fórmula no seguinte exemplo gráfico (ou ver as duas equações lado a lado aqui).<div class="simpleFunction" data="
// Cubic Hermite Curve. Same as SmoothStep()
y = xx(3.0-2.0*x);
// Quintic interpolation curve
//y = xxx(x(x*6.-15.)+10.);
"></div>
Note como o fim das curvas mudam. Você pode ler mais sobre isso nas próprias palavras de Ken.
Simplex noise
Para Ken Perlin, o sucesso de seu algoritmo não foi o suficiente, ele acreditava que podia ser mais performático. Na Siggraph de 2001, ele apresentou o "simplex noise", onde ele obteve o seguinte avanço sobre o algoritmo anterior:
* Um algoritmo com menor complexidade computacional e menos multiplicações.
* Um Noise que se escalona para maiores dimensões com menos custo computacional.
* Um Noise sem artefatos de direção.
* Um Noise com gradientes bem-definido e contínuos que podem ser computados a um baixo custo.
* Um algoritmo que é fácil de implementar em hardware.
Eu sei o que você está pensando... "Quem é este homem?!" Sim, seu trabalho é fantástico! Mas falando sério, como ele melhorou o algoritmo? Bem, nós vimos como interpolar os 4 pontos para duas dimensões (cantos de um quadrado); Então podemos convenientemente perceber que para três (ver a implementação aqui) e quatro dimensões nós precisamos interpolar 8 e 16 pontos. Certo? Em outras palavras, para N dimensões, nós precisamos interpolar suavemente 2 elevado a N pontos (2^N). Mas Ken espertamente notou que apesar de uma escolha óbvia de uma forma para o preenchimento espacial fosse o quadrado, a forma mais simples em 2D é o triângulo equilátero. Então ele começou a trocar a grade quadriculada (que acabamos de aprender a usar) por uma grade simplex de triângulos equiláteros.
A forma simplex para N dimensões é a forma com N + 1 vértices. Em outras palavras, um canto a menos para computar em 2D, 4 cantos a menos em 3D e 11 menos cantos em 4D! Isso é um grande aperfeiçoamento.
Em duas dimensões, a interpolação acontece de forma similar a um noise comum, ao interpolar os valores dos cantos para cada seção. Mas neste caso, ao usar uma grade simples, nós apenas precisamos interpolar a soma de 3 cantos.
Como uma grade simplex é feita? Em outro brilhante e elegante movimento, a grade simplex pode ser obtida por subdividir as células de uma grade de 4 cantos em dois triângulos isósceles e então incliná-los até que cada triângulo se torne equilátero.
Então, como Stefan Gustavson descreve em seu artigo: _"...Ao olhar para as partes inteiras das coordenadas transformadas (x,y) para o ponto que queremos avaliar, nós podemos rapidamente determinar qual célula das dois simplexos contém o ponto. Também, ao comparar as magnitudes de x e y, nós podemos determinar se o ponto está no simplex superior ou inferior, e cruzar os três pontos corretos."_
No código a seguir, você pode comentar a linha 44 para ver como a grade está inclinada, e então descomentar a linha 47 para ver como uma grade simplex pode ser construída. Note na linha 22 como estamos subdividindo o quadrado inclinado em dois triângulos equiláteros apenas detectando se
`x > y` (triângulo "inferior") ou `y > x` (triângulo "superior").<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Todas essas melhorias resultam em uma obra-prima conhecida como Simplex Noise. A seguir, temos uma implementação GLSL deste algoritmo feita por Ian McEwan e Stefan Gustavson (e apresentada neste artigo) que é complicado demais para propósitos educacionais, mas você ficará feliz em clicar e ver que é menos enigmático que você esperava, o código é curto e rápido.
Bom... Chega de detalhes técnicos, está na hora de você usar esses recursos na sua própria maneira de se expressar:
* Contemple como cada implementação de noise se parece. Imagine elas como uma matéria crua, como uma pedra de mármore para um escultor. O que você pode dizer sobre o "sentimento" que cada uma tem? Feche seus olhos para ativar sua imaginação, como se você quisesse encontrar formas em uma nuvem, o que você vê? De quê você se lembra? Em que você imagina que cada implementação de noise pode ser feita? Sigua sua coragem e tente criá-la em código.
* Faça um shader que projeta a ilusão de fluidez. Como um abajur de lava, pingos de tinta, água, etc.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Use o Simplex Noise para adicionar algumas texturas a um trabalho que você tenha feito.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
Neste capítuo, nós introduzimos um controle sobre o caos. Não foi uma tarefa fácil! Se tornar um mestre de dobra de noise toma tempo e esfoço.
Nos capítulos a seguir, nós veremos algumas técnicas conhecidas para aperfeiçoar suas habilidades de noise para criar designs generativo com shaders. Até então, aproveite um tempo do lado de fora, contemplando a natureza e seus padrões intrínsecos. Sua habilidade em observar precisa de uma dedicação igual (ou talvez maior) do que suas habilidades de construção. Vá lá fora e aproveite o resto do seu dia!
<p style="text-align:center; font-style: italic;">"Converse com a árvore, faça amizade com ela." Bob Ross
</p>
---
11/README Tr (11/README-tr.md)
Gürültü (Noise)
Mola zamanı! TV beyaz gürültüsüne benzeyen rastgele fonksiyonlarla oynuyorduk, shader'lar hakkında düşünmekten kafamız hâlâ dönüyor ve gözlerimiz yoruldu. Yürüyüşe çıkma zamanı!
Cildimizdeki havayı, yüzümüzdeki güneşi hissediyoruz. Dünya çok canlı ve zengin bir yer. Renkler, dokular, sesler. Yürürken yolların, kayaların, ağaçların ve bulutların yüzeyini fark etmekten kaçınamıyoruz.
Bu dokuların öngörülemezliğine "rastgele" denilebilir, ama daha önce oynadığımız rastgeleye benzemiyor. "Gerçek dünya" çok zengin ve karmaşık bir yer! Bu çeşitliliğe hesaplamalı olarak nasıl yaklaşabiliriz?
Bu, Ken Perlin'in 1980'lerin başında "Tron" filmi için daha gerçekçi dokular üretmekle görevlendirildiğinde çözmeye çalıştığı soruydu. Buna yanıt olarak, zarif bir Oscar ödüllü gürültü algoritması geliştirdi. (Küçük bir şey.)
Aşağıdaki klasik Perlin gürültü algoritması değildir, ancak gürültü üretmeyi anlamak için iyi bir başlangıç noktasıdır.
<div class="simpleFunction" data="
float i = floor(x); // tam sayı
float f = fract(x); // kesir
y = rand(i); //rand() önceki bölümde açıklandı
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
Bu satırlarda önceki bölümde yaptığımıza benzer bir şey yapıyoruz. Sürekli bir kayan sayıyı (
`x`) tam sayı (`i`) ve kesirli (`f`) bileşenlerine ayırıyoruz. `i`'yi elde etmek için `floor()` ve `f`'yi elde etmek için `fract()` kullanıyoruz. Sonra `x`'in tam sayı kısmına `rand()` uyguluyoruz, bu da her tam sayı için benzersiz bir rastgele değer veriyor.Bundan sonra iki yorum satırı görüyorsunuz. Birincisi her rastgele değeri doğrusal olarak enterpolasyon yapar.
glsly = mix(rand(i), rand(i + 1.0), f);
Devam edin ve bunun nasıl göründüğünü görmek için bu satırın yorumunu kaldırın. İki rastgele değeri
`mix()` etmek için f'de saklanan `fract()` değerini kullanıyoruz.Kitabın bu noktasında, doğrusal enterpolasyondan daha iyisini yapabileceğimizi öğrendik, değil mi?
Şimdi doğrusal yerine
`smoothstep()` enterpolasyonu kullanan aşağıdaki satırın yorumunu kaldırmayı deneyin.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Yorumunu kaldırdıktan sonra, tepeler arasındaki geçişin nasıl düzgünleştiğine dikkat edin. Bazı gürültü uygulamalarında programcıların
`smoothstep()` kullanmak yerine kendi kübik eğrilerini (aşağıdaki formül gibi) kodlamayı tercih ettiğini göreceksiniz.glslfloat u = f f (3.0 - 2.0 * f ); // özel kübik eğri
y = mix(rand(i), rand(i + 1.0), u); // enterpolasyonda kullanma
Bu düzgün rastgelelik, grafik mühendisleri veya sanatçılar için ezber bozucudur — organik hissiyatla görüntüler ve geometriler üretme yeteneği sağlar. Perlin'in Gürültü Algoritması, her türlü yaratıcı kullanım için büyüleyici eserler yapmak üzere farklı dillerde ve boyutlarda tekrar tekrar uygulanmıştır.
Şimdi sıra sizde:
* Kendi
`float noise(float x)` fonksiyonunuzu yapın.* Bir şekli taşıyarak, döndürerek veya ölçekleyerek canlandırmak için gürültü fonksiyonunuzu kullanın.
* Gürültü kullanarak birkaç şeklin birlikte 'dans ettiği' animasyonlu bir kompozisyon yapın.
* Gürültü fonksiyonunu kullanarak "organik görünümlü" şekiller oluşturun.
* "Yaratığınızı" elde ettikten sonra, ona belirli bir hareket atayarak bir karaktere dönüştürmeye çalışın.
2B Gürültü
Artık 1B'de gürültüyü nasıl yapacağımızı bildiğimize göre, 2B'ye geçme zamanı. 2B'de bir çizginin iki noktası arasında (
`rand(x)` ve `rand(x)+1.0`) enterpolasyon yapmak yerine, bir düzlemin kare alanının dört köşesi arasında enterpolasyon yapacağız (`rand(st)`, `rand(st)+vec2(1.,0.)`, `rand(st)+vec2(0.,1.)` ve `rand(st)+vec2(1.,1.)`).Benzer şekilde, 3B gürültü elde etmek istiyorsak bir küpün sekiz köşesi arasında enterpolasyon yapmamız gerekir. Bu teknik tamamen rastgele değerlerin enterpolasyonuyla ilgilidir, bu yüzden değer gürültüsü (value noise) olarak adlandırılır.
1B örneğinde olduğu gibi, bu enterpolasyon doğrusal değil, kübiktir; kare ızgaramızın içindeki herhangi bir noktayı düzgünce enterpolasyon yapar.
Aşağıdaki gürültü fonksiyonuna bir bakın.
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Izgaranın kareleri arasındaki enterpolasyonu görmek için uzayı 5'le ölçekleyerek (45. satır) başlıyoruz. Sonra gürültü fonksiyonunun içinde uzayı hücrelere ayırıyoruz. Hücrenin tam sayı konumunu ve hücre içindeki kesirli konumları saklıyoruz. Tam sayı konumunu dört köşenin koordinatlarını hesaplamak ve her biri için rastgele bir değer elde etmek için kullanıyoruz (23-26 arası satırlar). Son olarak, 35. satırda daha önce sakladığımız kesirli konumları kullanarak köşelerin 4 rastgele değeri arasında enterpolasyon yapıyoruz.
Şimdi sıra sizde. Aşağıdaki alıştırmaları deneyin:
* 45. satırın çarpanını değiştirin. Canlandırmayı deneyin.
* Hangi yakınlaştırma seviyesinde gürültü tekrar rastgele görünmeye başlar?
* Hangi yakınlaştırma seviyesinde gürültü algılanamaz hale gelir?
* Bu gürültü fonksiyonunu fare koordinatlarına bağlamayı deneyin.
* Ya gürültünün gradyanını bir mesafe alanı olarak ele alırsak? Onunla ilginç bir şey yapın.
* Artık düzen ve kaos üzerinde biraz kontrol elde ettiğinize göre, bu bilgiyi kullanmanın zamanı. Bir Mark Rothko tablosunun karmaşıklığına benzeyen dikdörtgenler, renkler ve gürültüden oluşan bir kompozisyon yapın.
Üretken Tasarımlarda Gürültü Kullanma
Gürültü algoritmaları başlangıçta dijital dokulara doğal bir je ne sais quoi vermek için tasarlandı. Şimdiye kadar gördüğümüz 1B ve 2B uygulamalar rastgele değerler arasında enterpolasyonlardı, bu yüzden Değer Gürültüsü olarak adlandırılırlar, ancak gürültü elde etmenin daha fazla yolu var...
Önceki alıştırmalarda keşfettiğiniz gibi, değer gürültüsü "bloklu" görünme eğilimindedir. Bu bloklu efekti azaltmak için 1985'te Ken Perlin Gradyan Gürültüsü adlı başka bir algoritma uygulaması geliştirdi. Ken, değerler yerine rastgele gradyanları enterpolasyon yapmanın yolunu buldu. Bu gradyanlar, tek değerler (
`float`) yerine yönler (bir `vec2` ile temsil edilen) döndüren 2B rastgele fonksiyonun sonucuydu. Kodu ve nasıl çalıştığını görmek için aşağıdaki görüntüye tıklayın.Inigo Quilez tarafından yapılan bu iki örneğe bir dakika ayırıp bakın ve değer gürültüsü ile gradyan gürültüsü arasındaki farklara dikkat edin.
Boyalarının pigmentlerinin nasıl çalıştığını anlayan bir ressam gibi, gürültü uygulamaları hakkında ne kadar çok bilirsek, onları o kadar iyi kullanabiliriz. Örneğin, düz çizgilerin oluşturulduğu uzayı döndürmek için iki boyutlu bir gürültü uygulaması kullanırsak, ahşaba benzeyen aşağıdaki kıvrımlı efekti üretebiliriz. Yine kodu görmek için görüntüye tıklayabilirsiniz.
glslpos = rotate2d( noise(pos) ) * pos; // uzayı döndür
pattern = lines(pos,.5); // çizgiler çiz
Gürültüden ilginç desenler elde etmenin başka bir yolu, onu bir mesafe alanı olarak ele almak ve Şekiller bölümünde açıklanan bazı numaraları uygulamaktır.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Siyah sıçrama
color -= smoothstep(.35,.4,noise(st*10.)); // Sıçramadaki delikler
Gürültü fonksiyonunu kullanmanın üçüncü bir yolu, bir şekli modüle etmektir. Bu da şekiller bölümünde öğrendiğimiz bazı teknikleri gerektirir.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Pratik yapmanız için:
* Başka hangi üretken deseni yapabilirsiniz? Ya granit? Mermer? Magma? Su? İlginizi çeken üç doku resmi bulun ve gürültü kullanarak algoritmik olarak uygulayın.
* Bir şekli modüle etmek için gürültü kullanın.
Ya hareket için gürültü kullanmak? Matris bölümüne geri dönün. "+"'yı hareket ettiren öteleme örneğini kullanın ve ona bazı rastgele ve gürültü* hareketleri uygulayın.
* Üretken bir Jackson Pollock yapın.
İyileştirilmiş Gürültü
Perlin'in orijinal simpleks olmayan gürültüsüne yaptığı iyileştirme olan Simpleks Gürültüsü, kübik Hermite eğrisinin ( _f(x) = 3x^2-2x^3_ ,
`smoothstep()` fonksiyonuyla aynıdır) quintic enterpolasyon eğrisiyle ( _f(x) = 6x^5-15x^4+10x^3_ ) değiştirilmesidir. Bu, eğrinin her iki ucunu daha "düz" yapar, böylece her sınır bir sonrakiyle zarif bir şekilde birleşir. Başka bir deyişle, hücreler arasında daha sürekli bir geçiş elde edersiniz. Aşağıdaki grafik örneğinde ikinci formülün yorumunu kaldırarak bunu görebilirsiniz (veya iki denklemi yan yana burada görün).<div class="simpleFunction" data="
// Kübik Hermite Eğrisi. SmoothStep() ile Aynı
y = xx(3.0-2.0*x);
// Quintic enterpolasyon eğrisi
//y = xxx(x(x*6.-15.)+10.);
"></div>
Eğrinin uçlarının nasıl değiştiğine dikkat edin. Daha fazlasını Ken'in kendi sözleriyle okuyabilirsiniz.
Simpleks Gürültüsü
Ken Perlin için algoritmasının başarısı yeterli değildi. Daha iyi performans gösterebileceğini düşündü. Siggraph 2001'de önceki algoritmaya göre aşağıdaki iyileştirmeleri elde ettiği "simpleks gürültüsünü" sundu:
* Daha düşük hesaplama karmaşıklığı ve daha az çarpma içeren bir algoritma.
* Daha düşük hesaplama maliyetiyle daha yüksek boyutlara ölçeklenen bir gürültü.
* Yönsel artefaktları olmayan bir gürültü.
* İyi tanımlanmış ve sürekli gradyanlara sahip, oldukça ucuza hesaplanabilen bir gürültü.
* Donanımda uygulanması kolay bir algoritma.
Ne düşündüğünüzü biliyorum... "Bu adam kim?" Evet, çalışması harika! Ama cidden, algoritmayı nasıl iyileştirdi? İki boyut için 4 noktayı (bir karenin köşelerini) enterpolasyon yaptığını gördük; bu yüzden üç boyut (burada bir uygulamayı görün) ve dört boyut için 8 ve 16 noktayı enterpolasyon yapmamız gerektiğini doğru tahmin edebiliriz. Başka bir deyişle N boyut için 2'nin N'inci kuvveti (2^N) noktayı düzgünce enterpolasyon yapmanız gerekir. Ancak Ken, uzay dolduran şekil için bariz seçenek kare olsa da, 2B'deki en basit şeklin eşkenar üçgen olduğunu zekice fark etti. Bu yüzden kare ızgarayı (nasıl kullanacağımızı yeni öğrendiğimiz) eşkenar üçgenlerden oluşan bir simpleks ızgara ile değiştirmeye başladı.
N boyut için simpleks şekli, N + 1 köşeli bir şekildir. Başka bir deyişle 2B'de bir köşe daha az, 3B'de 4 köşe daha az ve 4B'de 11 köşe daha az hesaplama! Bu büyük bir iyileştirme!
İki boyutta enterpolasyon, normal gürültüye benzer şekilde, bir bölümün köşelerinin değerlerini enterpolasyon yaparak gerçekleşir. Ancak bu durumda, bir simpleks ızgara kullanarak, sadece 3 köşenin toplamını enterpolasyon yapmamız gerekir.
Simpleks ızgarası nasıl yapılır? Başka parlak ve zarif bir hamlede, simpleks ızgarası, normal 4 köşeli bir ızgaranın hücrelerini iki ikizkenar üçgene bölerek ve sonra her üçgen eşkenar olana kadar eğerek elde edilebilir.
Sonra, Stefan Gustavson'un bu makalede anlattığı gibi: _"...değerlendirmek istediğimiz nokta için dönüştürülmüş koordinatların (x,y) tam sayı kısımlarına bakarak, noktayı içeren iki simpleksten hangisini hızlıca belirleyebiliriz. x ve y'nin büyüklüklerini de karşılaştırarak, noktanın üst veya alt simplekste olup olmadığını belirleyebilir ve doğru üç köşe noktasını gezebiliriz."_
Aşağıdaki kodda ızgaranın nasıl eğildiğini görmek için 44. satırın yorumunu kaldırabilir, ardından bir simpleks ızgaranın nasıl oluşturulabileceğini görmek için 47. satırın yorumunu kaldırabilirsiniz. 22. satırda eğilmiş kareyi sadece
`x > y` ("alt" üçgen) veya `y > x` ("üst" üçgen) tespit ederek iki eşkenar üçgene böldüğümüze dikkat edin.<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Tüm bu iyileştirmeler Simpleks Gürültüsü olarak bilinen algoritmik bir başyapıtla sonuçlanır. Aşağıdaki, Ian McEwan ve Stefan Gustavson tarafından yapılan bu algoritmanın GLSL uygulamasıdır (bu makalede sunulmuştur) ve eğitim amaçları için aşırı karmaşıktır, ancak tıklayıp beklediğinizden daha az şifreli olduğunu ve kodun kısa ve hızlı olduğunu görmekten mutlu olacaksınız.
Yeterince teknik detay... şimdi bu kaynağı kendi ifade biçiminizde kullanma zamanı:
* Her gürültü uygulamasının nasıl göründüğünü düşünün. Onları ham malzeme olarak, bir heykeltıraş için mermer kaya gibi hayal edin. Her birinin sahip olduğu "his" hakkında ne söyleyebilirsiniz? Bir bulutta şekiller bulmak istediğinizdeki gibi gözlerinizi kısın ve hayal gücünüzü tetikleyin. Ne görüyorsunuz? Size neyi hatırlatıyor? Her gürültü uygulamasının neye dönüştürülebileceğini hayal edin? İçgüdülerinizi takip edin ve kodda gerçekleştirmeyi deneyin.
* Akış illüzyonu yansıtan bir shader yapın. Lav lambası, mürekkep damlaları, su vb. gibi.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Daha önce yaptığınız bir çalışmaya doku eklemek için Simpleks Gürültüsü kullanın.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
Bu bölümde kaos üzerinde biraz kontrol sağladık. Kolay bir iş değildi! Gürültü bükme ustası olmak zaman ve çaba gerektirir.
Sonraki bölümlerde becerilerinizi mükemmelleştirmek ve shader'larla kaliteli üretken içerik tasarlamak için gürültünüzden daha fazlasını elde etmek üzere bazı iyi bilinen teknikleri göreceğiz. O zamana kadar, dışarıda doğayı ve karmaşık desenlerini düşünerek biraz zaman geçirmenin keyfini çıkarın. Gözlem yeteneğiniz, yapma becerileriniz kadar (veya muhtemelen daha fazla) özveri gerektirir. Dışarı çıkın ve günün geri kalanının keyfini çıkarın!
<p style="text-align:center; font-style: italic;">"Ağaçla konuş, onunla arkadaş ol." Bob Ross
</p>
#### Araç kutunuz için
* LYGIA'nın üretken fonksiyonları, GLSL'de desenler üretmek için yeniden kullanılabilir fonksiyonlar kümesidir. Üretken sanat yaratmak için rastgelelik ve gürültüyü nasıl kullanacağınızı öğrenmek için harika bir kaynaktır. Yeniden kullanılabilirlik, performans ve esneklik için tasarlanmış çok ayrıntılı bir kütüphanedir. Ve herhangi bir projeye ve framework'e kolayca eklenebilir.
---
11/README Vi (11/README-vi.md)
Nhiễu
Sau một hồi vật lộn với các hàm ngẫu nhiên nhìn như màn hình TV hỏng, ít nhiều thì ta cũng sẽ quay cuồng và hoa mắt, giờ thì cùng nghỉ ngơi và dạo chơi tí nhé.
Ta cảm nhận được gió thổi qua da, nắng ấm rọi lên mặt. Thế giới quả là sống động. Màu sắc, họa tiết rồi cả âm thanh nữa. Hãy nhìn bề mặt của những con đường, hòn đá, ngọn cây và cả những đám mây nữa.
họa tiết khó đoán của những bề mặt này có thể coi là "ngẫu nhiên", nhưng tuyệt nhiên không giống như sự lộn xộn ở chương trước. Thế giới thực quả là rất phức tạp ! Làm sao để ta lột tả được sự đa dạng đó bằng máy tính đây ?
Đó cũng chính là câu hỏi mà Ken Perlin đã đi tìm câu trả lời suốt những năm đầu thập niên 80, khi ông chịu trách nhiệm tạo ra các họa tiết tự nhiên hết mức có thể cho bộ phim "Tron". Và kết quả là ông đã nhận được một giải Oscar với thuật toán tạo nhiễu của mình.
Đoạn code dưới đây không hẳn là thuật toán tạo nhiễu Perlin cổ điển, nhưng là một bước đệm tốt để bắt đầu.
<div class="simpleFunction" data="
float i = floor(x); // phần nguyên
float f = fract(x); // phần thập phân
y = rand(i); // Hàm rand() được mô tả ở chương trước
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
Trông na ná như những gì ta đã làm ở chương trước. Ta tách riêng phần nguyên và phần thập phân của số thực
`x` vào hai biến `i` và `f` bằng hai hàm `floor()` và `fract()`. Sau đó chỉ sinh số ngẫu nhiên từ phần nguyên, kết quả sẽ giống nhau cho dù bạn có chạy bao nhiêu lần đi chăng nữa.Hai dòng cuối đã bị comment đi. Dòng đầu tiên sẽ nội suy tuyến tính 2 giá trị ngẫu nhiên ở 2 ô liên tiếp.
glsly = mix(rand(i), rand(i + 1.0), f);
Hãy uncomment xem đồ thị thay đổi thế nào. Tôi dùng chính phần thập phân có được từ hàm
`fract()` và lưu trong biến f phía trên, để quyết định tỉ lệ nội suy giữa 2 số dùng hàm `mix()`.Nếu bạn đã đọc tới đây, hẳn là bạn đã biết chúng ta có những cách nội suy khác ngoài tuyến tính phải không ?
Hãy thử uncomment dòng cuối cùng trong đoạn phía trên, tôi đã dùng hàm
`smoothstep()` thay cho nội suy tuyến tính đó.glsly = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
Bạn sẽ thấy các đỉnh bớt nhọn hơn. Ở rất nhiều thuật toán sinh nhiễu, bạn sẽ để ý thấy các lập trình viên thường sử dụng đường cong của riêng họ (ví dụ công thức dưới đây), thay vì dùng hàm
`smoothstep()`.glslfloat u = f f (3.0 - 2.0 * f ); // công thức đường cong bậc 3 với tham số riêng
y = mix(rand(i), rand(i + 1.0), u); // dùng kết quả để xác định tỉ lệ nội suy
Sự ngẫu nhiên có chuyển tiếp rất êm này thực sự là một cuộc cách mạng cho kỹ sư đồ hoạ và cả hoạ sỹ nữa - nó cho ta khả năng sinh ra các ảnh và hình khối có cảm giác hết sức tự nhiên. Thuật toán sinh nhiễu của Perlin (Perlin Noise) đã được code đi code lại trên các ngôn ngữ lập trình khác nhau để tạo nên vô vàn tác phẩm mê hoặc có tính sáng tạo cao.
Giờ tới lượt bạn:
* Hãy tự tạo một hàm
`float noise(float x)` của riêng mình.* Dùng chính hàm sinh nhiễu đó để di chuyển, quay hoặc thu phóng một hình vẽ.
* Kết hợp nhiều hình để xem chúng nhảy múa trên màn hình.
* Vẽ một sinh vật gì đó "trông tự nhiên".
* Khi đã có sinh vật của mình rồi, hãy thổi hồn cho nó bằng cách thêm 1 chuyển động nhé.
Nhiễu 2 chiều
Trên đây là cách tạo nhiễu 1 chiều, giờ hãy thử 2 chiều xem sao. Thay vì nội suy giữa 2 điểm
`fract(x)` và `fract(x)+1.0`, ở không gian 2 chiều, ta sẽ nội suy giữa 4 đỉnh của một tứ giác (`fract(st)`, `fract(st)+vec2(1.,0.)`, `fract(st)+vec2(0.,1.)` và `fract(st)+vec2(1.,1.)`).Tương tự như vậy, nếu ta muốn sinh nhiễu 3 chiều thì sẽ phải nội suy giữa 8 đỉnh của một khối lập phương (có thể vát). Kỹ thuật này hoàn toàn dựa trên phép nội suy giữa các điểm ngẫu nhiên cố định (value), nó được gọi là value noise.
Và cũng giống như phần sinh nhiễu 1 chiều, nếu sử dụng hàm nội suy bậc 3 (cubic) thay vì tuyến tính, thì ta sẽ thu được nhiễu rất mịn.
Hãy xem hàm sinh nhiễu dưới đây:
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Đầu tiên ta tạo lưới 5x5 (dòng 45) để nhìn rõ vùng chuyển tiếp giữa các ô. Trong hàm sinh nhiễu, ta xác định toạ độ của mỗi ô (số nguyên) để sinh số ngẫu nhiên đại diện cho 4 đỉnh (dòng từ 23 tới 26). Cuối cùng ở dòng 35 ta nội suy cả 4 giá trị dựa vào phần thập phân.
Giờ tới lượt bạn:
* Thử các kích thước lưới khác (dòng 45), và chuyển động nếu được
* Kích thước lớn tới đâu thì nhiễu trông giống như ngẫu nhiên
* Với kích thước nào thì không thể nhận ra nhiễu nữa
* Thử kết hợp hàm sinh nhiễu với toạ độ con trỏ chuột
* Nếu ta coi dải màu gradient sinh ra bởi nhiễu như là 1 distance field thì sao nhỉ ? Thử làm một cái gì đó hay ho với ý tưởng này xem sao.
* Giờ thì bạn kiểm soát được cả trật tự lẫn sự hỗn loạn ở một mức độ nào đó rồi, hãy thử xem bạn hiểu sâu tới đâu nhé. Hãy dùng nhiều hình chữ nhật với màu sắc khác nhau và nhiễu để mô phỏng lại bức tranh của Mark Rothko.
Ứng dụng nhiễu vào các thiết kế ngẫu nhiên
Các thuật toán sinh nhiễu ban đầu được thiết kế để thổi hồn vào các bức tranh kỹ thuật số. Các thuật toán 1 chiều và 2 chiều ta thấy trong chương này đều chỉ nội suy giữa các số ngẫu nhiên có sẵn, nên được gọi là Value Noise, nhưng còn nhiều cách khác để sinh nhiễu ...
Ở ví dụ phía trên, ta có thể thấy nhiễu sinh bởi nội suy trông như bị "vỡ ảnh". Để loại bỏ hiệu ứng này, Ken Perlin đã phát minh ra một thuật toán khác năm 1985 gọi là Gradient Noise. Ken tìm ra cách để nội suy giữa các dải màu gradient thay vì giữa các số cố định. Các dải màu này lại lại kết quả của một hàm sinh nhiễu 2 chiều khác. Click vào ảnh dưới đây để xem code của mẫu thiết kế này.
Hãy dành một chút thời gian để quan sát 2 ví dụ sau của Inigo Quilez để so sánh sự khác nhau giữa value noise và gradient noise.
Nếu một hoạ sỹ phải nắm rất rõ cách kết hợp màu trong các bức tranh, thì ta cũng phải hiểu tường tận các cách sinh nhiễu khác nhau thì mới tận dụng được. Ví dụ, nếu ta dùng nhiễu hai chiều để bẻ cong canvas đang có rất nhiều đường thẳng song song, ta có thể tạo nên vân bề mặt trông giống như gỗ vậy. Bạn có thể click vào ảnh dưới đây để xem code.
glslpos = rotate2d( noise(pos) ) * pos; // xoay trục toạ độ theo nhiễu
pattern = lines(pos,.5); // vẽ các đường thẳng
Một cách khác để tạo nên các họa tiết thú vị từ nhiễu là coi nó như 1 distance field và áp dụng các kỹ thuật được mô tả ở Chương Hình dạng.
glslcolor += smoothstep(.15,.2,noise(st*10.)); // Các giọt bắn
color -= smoothstep(.35,.4,noise(st*10.)); // Các vũng bắn
Cách thứ ba là dùng hàm sinh nhiễu để khiến các hình vẽ chuyển động. Để làm được thì ta cũng cần tới vài kỹ thuật được nhắc tới ở Chương Hình dạng.
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Để tập luyện:
* Bạn còn có thể tạo nên các hoa văn ngẫu nhiên nào nữa ? Vân đá ? Mắc ma ? Mặt nước ? Hãy tìm ảnh của những vân bề mặt bạn thích rồi dùng thuật toán để vẽ lại.
* Dùng nhiễu để bóp méo hình vẽ.
* Dùng nhiễu để di chuyển hình vẽ. Hãy quay lại Chương Ma trận và sửa code di chuyển hình chữ thập để áp dụng thêm nhiễu nhằm tạo ra những chuyển động khó đoán hơn.
* Vẽ lại bức tranh dưới đây của Jackson Pollock.
Nhiễu cải tiến
Perlin đã tự cải tiến thuật toán sinh nhiễu ban đầu của ông để tạo ra Simplex Noise, bằng cách thay thế đường cong Hermite( _f(x) = 3x^2-2x^3_ , cho kết quả giống với hàm
`smoothstep()`) bằng một đường cong bậc 5 (quintic) có công thức _f(x) = 6x^5-15x^4+10x^3_. Công thức nội suy này khiến cho hai đầu đường cong phẳng hơn và che được sự chuyển tiếp giữa 2 ô. Bạn có thể kiểm chứng điều đó bằng cách uncomment công thức thứ 2 ở đoạn code dưới đây (hoặc so sánh 2 phương trình cạnh nhau)<div class="simpleFunction" data="
// Đồ thị bậc 3 Hermite Curve, kết quả giống với hàm SmoothStep()
y = xx(3.0-2.0*x);
// Đồ thị bậc 5
//y = xxx(x(x*6.-15.)+10.);
"></div>
Chú ý hai đầu của đồ thị để dễ so sánh. Bạn có thể tìm hiểu thêm bằng nghiên cứu của chính Ken.
Simplex Noise
Đối với Ken Perlin thì sự thành công của thuật toán sinh nhiễu là chưa đủ, ông ấy nghĩ rằng có thể làm tốt hơn nữa. Ở hội nghị Siggraph 2001 ông đã thuyết trình thuật toán mới cải tiến hơn thuật toán cũ, gọi là "simplex noise":
* Hiệu quả hơn với ít phép tính hơn và độ phức tạp cũng thấp hơn.
* Sinh nhiễu ở các không gian nhiều chiều yêu cầu ít dữ liệu hơn thuật toán cũ.
* Các điểm ảnh dọc theo trục toạ độ không còn bị lộ nữa (vì quá gần với các điểm nội suy nên thường có giá trị tương đương)
* Các dải màu gradient dùng để sinh nhiễu có thể được tính rất nhanh và dễ kiểm soát
* Phần cứng có thể hỗ trợ để tối ưu thuật toán này dễ dàng
Tôi biết bạn đang tự hỏi ... "Đây là cao nhân phương nào?" Đúng vậy, các sản phẩm của ông thật siêu đẳng! Nhưng nói một cách nghiêm túc thì ông tự cải tiến thuật toán bằng cách nào ? Đầu tiên ông ấy quan sát cách sinh nhiễu ở không gian 2 chiều và thấy cần dùng 4 đỉnh, ở không gian 3 chiều sẽ cần 8 đỉnh và 4 chiều thì cần 16 đỉnh. Vậy là để sinh nhiễu ở không gian N chiều thì ta sẽ cần 2^N đỉnh. Và Ken đã nhanh chóng nhận ra rằng một tứ giác 4 đỉnh đâu phải hình học đơn giản nhất ở không gian 2 chiều đâu, đó phải là hình tam giác mới đúng. Và nếu muốn lấp đầy không gian bằng một lưới các tam giác thì tam giác đều sẽ giúp việc code đơn giản hơn. Ý tưởng cải tiến chỉ có vậy.
Và để sinh nhiễu cho không gian N chiều, ta sẽ chỉ cần N + 1 đỉnh. So với thuật toán cũ, thuật toán mới này giúp ta tiết kiệm 1 đỉnh ở không gian 2 chiều, 4 đỉnh ở không gian 3 chiều và 11 đỉnh ở không gian 4 chiều! Số lượng phép tính được cắt bớt cực kỳ nhiều. Quả là một cải tiến vượt bậc.
Lúc này, để tính toán giá trị nhiễu tại một điểm ảnh, ta chỉ cần mix giá trị ngẫu nhiên tại 3 đỉnh.
Vậy để tạo ra lưới tam giác (simplex grid) này thì làm thế nào ? Lại thêm một nước đi sáng suốt và tinh tế nữa, chỉ bằng cách chia đôi mỗi ô vuông ở lưới dạng bảng thông thường thành 2 nửa tam giác, rồi xô nghiêng (skew) cả lưới tới khi 3 cạnh của mỗi tam giác bằng nhau là được.
Stefan Gustavson đã mô tả thuật toán này trong báo cáo của ông như sau:
_"...chỉ cần kiểm tra phần nguyên trong toạ độ (x,y) của điểm ảnh ta cần tô màu, ta có thể nhanh chóng xác định nó ở ô vuông nào trong lưới dạng bảng. Rồi cũng chỉ cần so sánh x với y là ta biết được điểm ảnh nằm ở tam giác nào trong ô vuông đó, cuối cùng chỉ cần nội suy giá trị giữa 3 đỉnh tam giác là xong."_
Trong đoạn code dưới đây, bạn có thể uncomment dòng 44 để thấy lưới vuông bị xô nghiêng như thế nào, và uncomment tiếp dòng 47 để thấy một lưới tam giác được hình thành. Ở dòng 22, chỉ với một phép so sánh đơn giản
`x > y`, ta có thể xác định được điểm ảnh nằm ở tam giác nào trong số 2 tam giác tạo thành mỗi ô vuông.<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Các cải tiến này đã góp phần tạo nên một thuật toán kiệt tác gọi là Simplex Noise. Hình vẽ dưới đây được tạo bởi chính thuật toán này trong GLSL bởi Ian McEwan và Stefan Gustavson (và được mô tả trong báo cáo này) với một mức độ phức tạp quá tầm với một dự án giáo dục như thế này, nhưng nếu bạn click vào hình thì sẽ thấy ngạc nhiên là code rất ngắn gọn và tối ưu.
Chà... lý thuyết thế đủ rồi nhỉ, giờ hãy xắn tay áo lên nào:
* Hãy chiêm ngưỡng các vân bề mặt tuyệt đẹp của thiên nhiên và nghĩ xem có thể dùng thuật toán sinh nhiễu nào để mô phỏng không. Nếu cần thì cứ nheo mắt lại mà tưởng tượng như lúc đoán xem các đám mây có hình gì ấy.
* Tạo shader mô phỏng một dòng chảy, như dòng dung nham, giọt mực, dòng nước ...
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Sử dụng Simplex Noise để thêm vân bề mặt vào một hình vẽ nào đó bạn đã tạo ra.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
Ở chương này chúng tôi đã giới thiệu một vài kỹ thuật giúp kiểm soát sự hỗn loạn. Không dễ chút nào! Để trở thành một noise-bender thành thạo tốn rất nhiều thời gian và công sức.
Ở các chương tiếp theo ta sẽ thấy một vài kỹ thuật nổi tiếng khác giúp bạn hoàn thiện bộ kỹ năng của mình để bắt đầu thiết kế ra những tác phẩm có chất lượng cao bằng shader. Cho tới lúc đó, hãy tận hưởng thiên nhiên và cố gắng tìm ra các họa tiết ẩn giấu từ Mẹ Trái Đất nhé. Kỹ năng quan sát của bạn ít ra cũng phải tương đồng với kỹ năng chế tác. Hãy thư giãn nhé!
<p style="text-align:center; font-style: italic;">"Hãy làm bạn và nói chuyện với cái cây" - Bob Ross</p>
---
11/SUMMARY Pt (11/SUMMARY-pt.md)
Como podemos criar texturas com aparência mais natural como a superfície de estradas, pedras, árvores e nuvens? O ruído é a resposta.
Desde que Ken Perlin inventou seu primeiro algoritmo de ruído nos anos 80, a técnica vem sendo usada extensivamente na computação gráfica e simulações. Mesmo se você nunca ouviu falar desse nome, é impossível que não tenha visto. Vamos dar uma olhada passo a passo em como a função é construída e funciona. Podemos também cobrir uma versão mais eficiente do algoritmo chamado ruído simplex.
---
11/SUMMARY (11/SUMMARY.md)
How can we create more natural looking textures like surface of the roads, rocks, trees and clouds? Noise function is the answer.
Since Ken Perlin invented his first noise algorithm in 80s, the technique has been extensively used throughout computer graphics and simulations. Even if you have never heard of the name, it's not possible you have never seen it. Let's look step by step at how the function is built and works. We also cover more efficient version of the algorithm called simplex noise.
---
12/README (12/README.md)
Cellular Noise
In 1996, sixteen years after Perlin's original Noise and five years before his Simplex Noise, Steven Worley wrote a paper called “A Cellular Texture Basis Function”. In it, he describes a procedural texturing technique now extensively used by the graphics community.
To understand the principles behind it we need to start thinking in terms of iterations. Probably you know what that means: yes, start using
`for` loops. There is only one catch with `for` loops in GLSL: the number we are checking against must be a constant (`const`). So, no dynamic loops - the number of iterations must be fixed.Let's take a look at an example.
Points for a distance field
Cellular Noise is based on distance fields, the distance to the closest one of a set of feature points. Let's say we want to make a distance field of 4 points. What do we need to do? Well, for each pixel we want to calculate the distance to the closest point. That means that we need to iterate through all the points, compute their distances to the current pixel and store the value for the one that is closest.
glslfloat min_dist = 100.; // A variable to store the closest distance to a point min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
This is not very elegant, but it does the trick. Now let's re-implement it using an array and a
`for` loop.glslfloat m_dist = 100.; // minimum distance
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Note how we use a
`for` loop to iterate through an array of points and keep track of the minimum distance using a `min()` function. Here's a brief working implementation of this idea:<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
In the above code, one of the points is assigned to the mouse position. Play with it so you can get an intuitive idea of how this code behaves. Then try this:
- How can you animate the rest of the points?
- After reading the chapter about shapes, imagine interesting ways to use this distance field!
- What if you want to add more points to this distance field? What if we want to dynamically add/subtract points?
Tiling and iteration
You probably notice that
`for` loops and arrays are not very good friends with GLSL. Like we said before, loops don't accept dynamic limits on their exit condition. Also, iterating through a lot of instances reduces the performance of your shader significantly. That means we can't use this direct approach for large amounts of points. We need to find another strategy, one that takes advantage of the parallel processing architecture of the GPU.One way to approach this problem is to divide the space into tiles. Not every pixel needs to check the distance to every single point, right? Given the fact that each pixel runs in its own thread, we can subdivide the space into cells, each one with one unique point to watch. Also, to avoid aberrations at the edges between cells we need to check for the distances to the points on the neighboring cells. That's the main brillant idea of Steven Worley's paper. At the end, each pixel needs to check only nine positions: their own cell's point and the points in the 8 cells around it. We already subdivide the space into cells in the chapters about: patterns, random and noise, so hopefully you are familiar with this technique by now.
glsl// Scale
st *= 3.; // Tile the space
vec2 i_st = floor(st);
vec2 f_st = fract(st);
So, what's the plan? We will use the tile coordinates (stored in the integer coordinate,
`i_st`) to construct a random position of a point. The `random2f` function we will use receives a `vec2` and gives us a `vec2` with a random position. So, for each tile we will have one feature point in a random position within the tile.glslvec2 point = random2(i_st);
Each pixel inside that tile (stored in the float coordinate,
`f_st`) will check their distance to that random point.glslvec2 diff = point - f_st;
float dist = length(diff);
The result will look like this:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
We still need to check the distances to the points in the surrounding tiles, not just the one in the current tile. For that we need to iterate through the neighbor tiles. Not all tiles, just the ones immediately around the current one. That means from
`-1` (left) to `1` (right) tile in `x` axis and `-1` (bottom) to `1` (top) in `y` axis. A 3x3 region of 9 tiles can be iterated through using a double `for` loop like this one:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Neighbor place in the grid
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Now, we can compute the position of the points on each one of the neighbors in our double
`for` loop by adding the neighbor tile offset to the current tile coordinate.glsl...
// Random position from current + neighbor place in the grid
vec2 point = random2(i_st + neighbor);
...
The rest is all about calculating the distance to that point and storing the closest one in a variable called
`m_dist` (for minimum distance).glsl...
vec2 diff = neighbor + point - f_st; // Distance to the point
float dist = length(diff);
// Keep the closer distance
m_dist = min(m_dist, dist);
...
The above code is inspired by this article by Inigo's Quilez where he said:
"... it might be worth noting that there's a nice trick in this code above. Most implementations out there suffer from precision issues, because they generate their random points in "domain" space (like "world" or "object" space), which can be arbitrarily far from the origin. One can solve the issue moving all the code to higher precision data types, or by being a bit clever. My implementation does not generate the points in "domain" space, but in "cell" space: once the integer and fractional parts of the shading point are extracted and therefore the cell in which we are working identified, all we care about is what happens around this cell, meaning we can drop all the integer part of our coordinates away all together, saving many precision bits. In fact, in a regular voronoi implementation the integer parts of the point coordinates simply cancel out when the random per cell feature points are subtracted from the shading point. In the implementation above, we don't even let that cancelation happen, cause we are moving all the computations to "cell" space. This trick also allows one to handle the case where you want to voronoi-shade a whole planet - one could simply replace the input to be double precision, perform the floor() and fract() computations, and go floating point with the rest of the computations without paying the cost of changing the whole implementation to double precision. Of course, same trick applies to Perlin Noise patterns (but i've never seen it implemented nor documented anywhere)."
Recapping: we subdivide the space into tiles; each pixel will calculate the distance to the point in their own tile and the surrounding 8 tiles; store the closest distance. The result is a distance field that looks like the following example:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Explore this further by:
- Scaling the space by different values.
- Can you think of other ways to animate the points?
- What if we want to compute an extra point with the mouse position?
- What other ways of constructing this distance field can you imagine, besides
`m_dist = min(m_dist, dist);`?
- What interesting patterns can you make with this distance field?This algorithm can also be interpreted from the perspective of the points and not the pixels. In that case it can be described as: each point grows until it finds the growing area from another point. This mirrors some of the growth rules in nature. Living forms are shaped by this tension between an inner force to expand and grow, and limitations by outside forces. The classic algorithm that simulates this behavior is named after Georgy Voronoi.
Voronoi Algorithm
Constructing Voronoi diagrams from cellular noise is less hard than what it might seem. We just need to keep some extra information about the precise point which is closest to the pixel. For that we are going to use a
`vec2` called `m_point`. By storing the vector direction to the center of the closest point, instead of just the distance, we will be "keeping" a "unique" identifier of that point.glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Note that in the following code that we are no longer using
`min` to calculate the closest distance, but a regular `if` statement. Why? Because we actually want to do something more every time a new closer point appears, namely store its position (lines 32 to 37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Note how the color of the moving cell (bound to the mouse position) changes color according to its position. That's because the color is assigned using the value (position) of the closest point.
Like we did before, now is the time to scale this up, switching to Steven Worley's paper's approach. Try implementing it yourself. You can use the help of the following example by clicking on it. Note that Steven Worley's original approach uses a variable number of feature points for each tile, more than one in most tiles. In his software implementation in C, this is used to speed up the loop by making early exits. GLSL loops don't allow variable number of iterations, so you probably want to stick to one feature point per tile.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Once you figure out this algorithm, think of interesting and creative uses for it.
Improving Voronoi
In 2011, Stefan Gustavson optimized Steven Worley's algorithm to GPU by only iterating through a 2x2 matrix instead of 3x3. This reduces the amount of work significantly, but it can create artifacts in the form of discontinuities at the edges between the tiles. Take a look to the following examples.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Later in 2012 Inigo Quilez wrote an article on how to make precise Voronoi borders.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Inigo's experiments with Voronoi didn't stop there. In 2014 he wrote this nice article about what he calls voro-noise, a function that allows a gradual blend between regular noise and voronoi. In his words:
"Despite this similarity, the fact is that the way the grid is used in both patterns is different. Noise interpolates/averages random values (as in value noise) or gradients (as in gradient noise), while Voronoi computes the distance to the closest feature point. Now, smooth-bilinear interpolation and minimum evaluation are two very different operations, or... are they? Can they perhaps be combined in a more general metric? If that was so, then both Noise and Voronoi patterns could be seen as particular cases of a more general grid-based pattern generator?"
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Now it's time for you to look closely at things, be inspired by nature and find your own take on this technique!
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
#### For your toolbox
* LYGIA's generative functions are a set of reusable functions to generate patterns in GLSL. It's a great resource to learn how to use randomness and noise to create generative art. It's very granular library, designed for reusability, performance and flexibility. And it can be easily be added to any projects and frameworks.
---
12/README De (12/README-de.md)
Zelluläres Rauschen
Im Jahre 1996, 16 Jahre nach Veröffentlichung von Perlins ursprünglichem Noise-Algorithmus und fünf Jahre vor der Erfindung des Simplex Noise-Algorithmus, schrieb Steven Worley einen Artikel mit dem Titel „Eine Basisfunktion für zelluläre Texturen“. Darin beschreibt er eine Technik zur prozeduralen Texturierung von Flächen, die heute im Bereich der Computergrafik nicht mehr wegzudenken ist.
Um das Prinzip hinter diesem Verfahren zu verstehen, müssen wir die Abläufe aus dem Blickwinkel von Iterationen betrachten. Als Programmierer kannst Du Dir vielleicht denken, was das bedeutet: Ja, genau, es geht um Schleifen und um den Einsatz des
`for`-Befehls. Allerdings gibt es da einen wichtigen Aspekt in GLSL: Die Anzahl der Schleifendurchläufe muss durch eine Konstante (`const`) vorgegeben sein. Sie ist also nicht dynamisch, sondern steht grundsätzlich von vornherein fest.Lass uns dazu am besten ein Beispiel anschauen.
Punkte für ein Distanzfeld
Zelluläres Rauschen basiert auf Distanzfeldern, konkret auf der Berechnung der Entfernung zum nächstgelegenen Bezugspunkt aus einer gegebenen Menge von Punkten. Lass uns annehmen, wir wollten ein Distanzfeld aus vier Punkten erzeugen. Was benötigen wir dafür? Nun, für jeden zu berechnenden Bildpunkt auf unserer Zeichenfläche wollen wir die Entfernung zum nächstgelegenen der vier Bezugspunkte berechnen . Das bedeutet, dass wir alle vier Bezugspunkte durchlaufen, ihre Entfernung zum aktuell bearbeiteten Pixel berechnen und uns die kleinste dieser Entfernungen merken müssen.
glslfloat min_dist = 100.; // speichert die kleineste Entf. zu einem der 4 Bezugspunkte min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
Diese Lösung ist natürlich nicht besonders elegant, aber sie erfüllt ihren Zweck. Lass uns das Ganze nun mit Hilfe eines Arrays und einer
`for`-Schleife realisieren.glslfloat m_dist = 100.; // speichert die minimale Entfernung
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Beachte, wie wir hier die
`for`-Schleife einsetzen, um ein Array mit Bezugspunkten zu durchlaufen, und die bislang kleinste Entfernung mit Hilfe der `min()`-Funktion festhalten. Hier folgt eine vollständige Umsetzung dieses Ansatzes in GLSL.<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
In dem obigen Shader wird einer der Bezugspunkte des Distanzfelds auf die Mausposition über der Zeichenfläche gesetzt. Fahre mit der Maus über die Fläche und spiele ein wenig damit herum. So bekommst Du am besten eine Vorstellung davon, wie der Code funktioniert. Probiere anschließend folgendes aus:
- Gelingt es Dir, die Position der anderen Bezugspunkte zu animieren?
- Nachdem Du das Kapitel über Formen ja vermutlich bereits gelesen hast, stelle Dir einen interessanten Weg vor, was man mit dem vorliegenden Distanzfeld anstellen könnte.
- Was muss man tun, um das Distanzfeld um weitere Bezugspunkte zu erweitern? Wie können wir dynamisch einzelne Bezugspunkte hinzufügen oder entfernen?
Kachelung und Wiederholung
Vielleicht ist Dir schon klargeworden, dass
`for` Schleifen und Arrays nicht die besten Freunde von GLSL sind. Wie schon gesagt: Schleifen in GLSL akzeptieren keine variablen Limite für die Anzahl der Durchläufe. Außerdem verlangsamen vielfache Schleifendurchläufe die Ausführung Deiner Shader spürbar, da Schleifen nicht vorzeitig beendet werden können. Das hat zur Folge, dass dieser Ansatz für Distanzfelder mit einer Vielzahl von Bezugspunkten untauglich ist. Wir müssen einen anderen Weg wählen, einen, der einen Vorteil aus der Parallelverarbeitung der GPU zieht.Ein Ansatz, um sich dieser Herausforderung zu stellen, ist die Unterteilung der Zeichenfläche in einzelne Kacheln bzw. Zellen. Nicht jeder Pixel muss die Entfernung zu allen Punkten des Distanzfeldes überprüfen. Manche sind auf jeden Fall zu weit entfernt, um ein Minima zu liefern. Dies gilt beispielsweise für alle Zellen, die nicht direkt an die aktuelle Zelle angrenzen.
Weil die Farbe für jeden Pixel in einem eigenen Thread berechnet wird, können wir die Zeichenfläche in einzelne Zellen unterteilen - jede mit einem Bezugspunkt.
Um Anomalien an den Schnittflächen zwischen den Zellen zu vermeiden, müssen wir jeweils die Entfernung zum Bezugspunkt der benachbarten Zellen überprüfen. Das ist im Wesentlichen die brillante Idee hinter dem Ansatz von Steven Worley.
Letztendlich muss jeder Pixel nur die Entfernung zu neun Bezugspunkten berechnen: Dem seiner eigenen Zelle und jene der acht umliegenden Zellen. Alle anderen Zellen sind zu weit entfernt.
Wir haben bereits in den Kapiteln über Muster, Generative Designs und Rauschen gesehen, wie man die Zeichenfläche in einzelne Zellen unterteilt, von daher bist Du mit diesem Prinzip wahrscheinlich schon vertraut.
glsl// den Raum aufblaehen ...
st *= 3.; // ... und in Zellen unterteilen
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Also, wie lautet der Plan? Wir werden die Koordinate der Kachel/Zelle (die in dem Integer-Vektor
`i_st` gespeichert ist) nutzen, um einen zufälligen Punkt zu erzeugen. Wir setzen dafür die `random2f`-Funktion ein, die einen `vec2` als Parameter erhält und einen `vec2` mit einer Zufallsposition zurückliefert. So erhalten wir für jede Zelle einen Bezugspunkt mit einer zufälligen Position innerhalb der Zelle.glslvec2 point = random2(i_st);
Der jeweils zu zeichnende Bildpunkt innerhalb der Zelle (gespeichert in dem Fließkommavektor
`f_st`) wird seine Entfernung zu diesem zufällig gewählten Bezugspunkt berechnen.glslvec2 diff = point - f_st;
float dist = length(diff);
Das Ergebnis sieht dann wie folgt aus:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Aber wir wollen ja zusätzlich noch die Entfernung zu den Bezugspunkten in den umliegenden Zellen einbeziehen. Dafür müssen wir diese Zellen durchlaufen. Aber nicht alle, sondern nur die unmittelbar angrenzenden. Das heißt die Zellen mit den Abständen von
`-1` (links) bis `1` (rechts) entlang der `x`-Achse, sowie die Zellen mit den Abständen von `-1` (unten) bis `1` (oben) entlang der `y`-Achse. Dieser Bereich von 3x3 Zellen lässt sich leicht mit Hilfe einer doppelten `for`-Schleife abarbeiten, so wie im Folgenden gezeigt:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// benachbarte Zelle innerhalb des Rasters
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Nun können wir die Bezugspunkte aus jeder der benachbarten Zellen in unserer doppelten
`for`-Schleife berechnen, indem wir den Versatz aus der Variablen `neighbour` zu den Koordinaten der aktuellen Zelle addieren.glsl...
// Zufallsposition von der aktuellen + der benachbarten Zelle im Raster
vec2 point = random2(i_st + neighbor);
...
Dann bleibt nur noch, die Entfernung vom aktuell zu zeichnenden Punkt zu dem jeweiligen Bezugspunkt zu berechnen und die geringste Entfernung in der Variablen
`m_dist` (für „minimale Distanz“) zu speichern.glsl...
vec2 diff = neighbor + point - f_st; // Entfernung zu diesem Punkt
float dist = length(diff);
// die kleinste Entfernung speichern
m_dist = min(m_dist, dist);
...
Der obige Programmcode wurde durch einen Artikel von Inigo Quilez inspiriert. Dort schreibt er:
„... es ist vielleicht interessant darauf hinzuweisen, dass in dem obigen Code ein netter Trick steckt. Die meisten Implementationen dieses Algorithmus leiden unter einer schlechten Präzision der Berechnungen, weil sie die zufälligen Bezugspunkte auf den gesamten Koordinatenraum beziehen, so dass die Koordinaten sehr weit vom Ursprung entfernt sind. Man kann dagegen ansteuern, indem man Variablentypen mit besonders hoher Genauigkeit verwendet, was sich jedoch negativ auf die Geschwindigkeit der Berechnungen auswirkt. Oder man macht es etwas cleverer, indem man die Koordinaten nicht auf den gesamten Koordinatenraum bezieht, sondern auf die Ebene der einzelnen Zellen: Sobald der ganzzahlige Teil und der Nachkommateil des zu zeichnenden Punktes berechnet sind und dadurch die Zelle feststeht, in der sich der Punkt befindet, beschäftigen wir uns nur noch damit, was um diese Zelle herum geschieht. Dadurch müssen wir uns nicht mehr um den ganzzahligen Teil der Koordinaten kümmern, wodurch man viele Bits bei den weiteren Berechnungen einspart. Tatsächlich steuern bei herkömmlichen Voronoi-Implementierungen die ganzzahligen Anteile der Punktkoordinaten ebenfalls dem Wert 0 entgegen, sobald die zufälligen Bezugspunkte der Zellen vom aktuell zu zeichnenden Punkt abgezogen werden. In der obigen Implementation lassen wir es gar nicht erst so weit kommen, weil wir alle Koordinatenberechnungen auf den Raum der Zellen beziehen. Mit diesem Trick kann man sogar einen ganzen Planeten mit derartig geformten Voronoi-Zellen überziehen, indem man die Punktkoordinaten einfach in doppelter Fließkommagenauigkeit darstellt, die Berechnungen von
`floor()` und `fract()` durchführt, und dann mit einfacher Fließkommagenauigkeit fortfährt. So erspart man sich den (Zeit-) Aufwand, die gesamte Berechnung mit doppelter Fließkommagenauigkeit auszuführen. Natürlich kann man diesen Trick auch auf Perlins Noise-Algorithmus anwenden (allerdings habe ich noch nie ein solche Implementation gesehen). “Um es noch einmal zusammenzufassen: Wir unterteilen den Raum in einzelne Zellen. Für jeden zu zeichnenden Punkt berechnen wir die kleinste Entfernung zum Bezugspunkt seiner Zelle bzw. zu den Bezugspunkten der acht umliegenden Zellen. Als Ergebnis erhalten wir ein Distanzfeld, so wie in dem folgenden Beispiel:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Experimentiere damit, indem Du:
- den Raum in mehr Zellen aufteilst.
- Dir andere Möglichkeiten ausdenkst, um die Bezugspunkte zu animieren.
- einen der Bezugspunkte durch die aktuelle Mausposition ersetzt.
- über andere Wege nachdenkst, um das Distanzfeld zu berechnen, abseits von
`m_dist = min(m_dist, dist);`.
- untersuchst, welche anderen interessanten Muster sich über dieses Distanzfeld erzeugen lassen.Man kann diesen Algorithmus nicht nur aus der Perspektive der jeweils zu zeichnenden Pixel betrachten, sondern auch aus Sicht der Bezugspunkte. In diesem Fall lässt sich der Algorithmus so beschreiben: Jeder Bezugspunkt wächst aus seiner Mitte heraus, bis er an die Grenzen eines anderen wachsenden Bezugspunktes stößt. Das spiegelt das Wachstum biologischer Zellen in der Natur wieder. Lebendige Organismen werden von dieser Spannung zwischen dem inneren Antrieb zum Wachstum und äußeren Begrenzungen geformt. Der klassische Algorithmus, der dieses Verhalten nachahmt, ist nach Georgi Feodosjewitsch Woronoi, engl „Georgy Voronoi“ benannt.
Der Voronoi-Algorithmus
Die Erzeugung von Voronoi-Diagrammen auf Basis von zellulärem Rauschen ist weniger kompliziert, als es vielleicht erscheint. Wir müssen nur zusätzliche Informationen über den Bezugspunkt festhalten, der dem zu zeichnenden Punkt am nächsten liegt. Dafür verwenden wir eine Variable vom Typ
`vec2` mit dem Namen `m_point`. Darin speichern wir den Vektor zum nächstgelegenen Bezugspunkt und nicht einfach nur dessen Entfernung. So behalten wir ein eindeutiges Identifikationsmerkmal für diesen Punkt.glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Bitte beachte, dass wir in dem folgenden Programmcode die geringste Entfernung nicht mehr mit Hilfe der
`min`-Funktion berechnen, sondern einen herkömmlichen `if`-Befehl einsetzen. Warum wir das tun? Weil wir diesmal etwas mehr unternehmen wollen, sobald ein neuer näherliegender Punkt auftaucht, nämlich seine Position speichern (Programmzeilen 32 bis 37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Du wirst sehen, dass die Farbe der beweglichen Zelle (die dem Mauszeiger folgt) auf Basis ihrer Position wechselt. Die Ursache dafür ist, dass hier die Farbe aufgrund des Wertes (der Position) des nächstgelegenen Bezugspunktes zugewiesen wird.
Genau wie zuvor ist es nun an der Zeit, das Ganze zu erweitern, indem wir zu dem Algorithmus aus dem Papier von Steven Worley übergehen. Versuche doch einmal selbst, diesen Algorithmus zu implementieren. Du kannst dabei auf das folgende Beispiel zurückgreifen, indem Du darauf klickst.
Bitte beachte, dass der ursprüngliche Ansatz von Steven Worley eine variable Anzahl von Bezugspunkten für jede Zelle vorsieht. In seiner Implementation des Algorithmus in C nutzt er dies für einen zeitigen Abbruch der Schleife. Schleifen in GLSL erlauben jedoch keinen vorzeitigen Ausstieg oder eine variable Anzahl von Schleifendurchläufen, deshalb wirst Du vielleicht besser bei einem Bezugspunkt pro Zelle bleiben.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Sobald Du die Funktionsweise dieses Algorithmus verstanden hast, kannst Du über interessante und kreative Einsatzmöglichkeiten nachdenken.
Verbesserung des Voronoi-Algorithmus
Im Jahre 2011 hat Stefan Gustavson eine Optimierung von Steven Worleys Algorithmus für GPUs vorgeschlagen, bei der nur noch eine 2x2 Matrix benachbarter Zellen untersucht wird, an Stelle der bisherigen 3x3 Matrix. Das reduziert den Rechenaufwand für jeden Punkt deutlich, kann aber zur Artefakten durch unsaubere Übergänge an den Grenzen der Zellen führen. Schau Dir die folgenden Beispiele an.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Im Jahre 2012 präsentierte Inigo Quilez einen interessanten Artikel über die Erzeugung präziser Voronoi-Abgrenzungen.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Inigos Experimente zu diesem Thema hörten damit nicht auf. Im Jahr 2014 verfasste er einen schönen Beitrag über das, was er als Voro-Noise, dt. „Voro-Rauschen“ bezeichnet - eine Funktion, die einen graduellen Übergang zwischen normalem Rauschen und Voronoi-Rauschen ermöglicht. Er schrieb:
„Abgesehen von ihrer Ähnlichkeit ist es entscheidend, dass das Raster aus Zellen in beiden Mustern unterschiedlich verwendet wird. Interpoliertes Rauschen mit Zufallswerten (wie bei Value-Noise) oder mit Gradienten (wie bei Gradient-Noise) unterscheidet sich von Voronoi, wo es auf die Entfernung zum nächstgelegenen Bezugspunkt ankommt. Schließlich sind die bilineare Interpolation und die Minima-Berechnung zwei ganz unterschiedliche Operationen, nicht wahr? Doch vielleicht kann man sie in einem größeren Rahmen vereinigen? Sollte das möglich sein, könnte man sowohl Rauschmuster als auch Voronoi-Muster als Spezialfälle eines allgemeineren rasterbasierten Mustergenerators betrachten.“
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Nun ist die Zeit gekommen, dass Du Dir die Dinge genau anschaust, Dich von der Natur inspirieren lässt und Deine eigene Nutzungsmöglichkeiten dieser Techniken entdeckst!
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
---
12/README Fr (12/README-fr.md)
Bruit Cellulaire
En 1996, seize ans après le Bruit de Perlin et cinq ans avant le Bruit Simplex, Steven Worley a publié un papier : “Une function pour les textures cellulaires”.
Il y décrit une technique de texture procédurale amplement utilisée de nos jours par la communauté des développeurs graphiques.
Pour comprendre le principe qui sous-tend l'algorithme, nous devons réfléchir en termes d'itérations.
Un champ de distances pour quelques points
Imaginons que nous voulions faire un champ de distance à quatre points.
De quoi aurions nous besoin ? Pour faire court, pour chaque pixel, trouver la distance au point le plus proche.
Ce qui signifie qu'il va nous falloir itérer sur tous les points et conserver la distance au point le plus proche.
glslfloat m_dist = 1.; // distance minimum
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Pour ce faire, nous pouvons utiliser une boucle
for pour itérer sur un tableau de points et conserver la distance minimum en nous servant de la fonction `min()`.
Voici une implémentation sommaire :<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
Notez que dans l'exemple ci dessus, l'un des points utilise la position de la souris.
Jouez avec pour vous donner une meilleure idée de comment ça marche puis essayez ceci :
* Comment animer les reste des points ?
* Après avoir lu le chapitre sur les formes, imaginez des façons intéressantes d'utiliser un champ de distances.
* comment ajouter plus de points au champ de distances ? Que se passe-t'il si on souhaite ajouter/enlever des points dynamiquement ?
Pavage et itérations
vous avez sans doute remarqué que les boucles
for et l'utilisation de tableaux n'est pas recommandée en GLSL.
Les boucles n'accpetent pas d'arguments dynamiques, itérer sur un grand nombre d'entrées ralentit considérablement l'exécution du shader, nous devons donc changer de stratégie.Une façon d'aborder le problème est de diviser l'espace en tuiles (tiles) ou cellules.
Au fond, tous les pixels n'ont pas besoin de tester l'ensemble des points n'est ce pas ?
Ils ont simplement besoin de vérifier les points qui sont proches d'eux.
C'est l'idée principale de la publication de Steven Worley.
Nous avons déjà subdivisé l'espace en cellules au chapitres des motifs, de l'aléatoire et du bruit.
Vous devriez être un peu plus familier avec cette technique.
glsl// échelle (nombre de cellules)
st *= 3.; // variables utilisées pour paver le plan
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Quel plan ? Nous allons nous servir des coordonnées des cellules (stockées dans la partie entières
i_st) pour constuire la position de nos points.
La méthode `random2f` recevra un `vec2` et nous retournera un `vec2`.
Donc chaque cellule contiendra un point à une position aléatoire dans la cellule.glslvec2 point = random2(i_st);
A l'intérieur de chaque cellule, tous les fragments (à l'aide de la partie fractionelle de
`f_st`, fract(st) ) vont mesurer la distance à ce point.glslvec2 diff = point - f_st;
float dist = length(diff);
Ca ressemblera à :
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Nous devons également calculer la distance aux points des cellules adjacentes ;
en effet, il se pourrait que le point le plus proche du fragment en cours d'évaluation se trouves dans une cellule voisine.
Pour cela, nous allons itérer sur les cellules voisines, pas toutes les cellules, juste les cellules adjacentes.
Soit de
`-1` (gauche) à `1` (droite) cellules en `x` et de `-1` (bas) à `1` (haut) en `y`.
Ce qu'on appelle un kernel de 3x3, sur lequel on peut itérer grâce à deux boucles for imbriquées telle que celle-ci.glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// cellules voisines
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Nous pouvons prévoir la position des points des cellules adjacentes en ajoutant les deltas
x et y de la boucle for aux indices de la cellule courante.
On peut s'en servir pour calculer la position du point de la cellule en cours de traitement dans la boucle grâce à la méthode random2() décrite plus haut.glsl...
// Random position from current + neighbor place in the grid
vec2 point = random2(i_st + neighbor);
...
Pour le reste, il suffit de calculer la distance entre ce point et le fragment et de conserver le minimum dans une variable
`m_dist` (minimal distance).glsl...
vec2 diff = neighbor + point - f_st; // Distance au point
float dist = length(diff);
// conserve la distance la plus courte
m_dist = min(m_dist, dist);
...
Le code ci-dessus s'inspire de cet article d'Inigo Quilez où il dit :
"... it might be worth noting that there's a nice trick in this code above. Most implementations out there suffer from precision issues, because they generate their random points in "domain" space (like "world" or "object" space), which can be arbitrarily far from the origin. One can solve the issue moving all the code to higher precision data types, or by being a bit clever. My implementation does not generate the points in "domain" space, but in "cell" space: once the integer and fractional parts of the shading point are extracted and therefore the cell in which we are working identified, all we care about is what happens around this cell, meaning we can drop all the integer part of our coordinates away all together, saving many precision bits. In fact, in a regular voronoi implementation the integer parts of the point coordinates simply cancel out when the random per cell feature points are subtracted from the shading point. In the implementation above, we don't even let that cancelation happen, cause we are moving all the computations to "cell" space. This trick also allows one to handle the case where you want to voronoi-shade a whole planet - one could simply replace the input to be double precision, perform the floor() and fract() computations, and go floating point with the rest of the computations without paying the cost of changing the whole implementation to double precision. Of course, same trick applies to Perlin Noise patterns (but i've never seen it implemented nor documented anywhere)."
En résumé : on subdivise l'espace en cellules, chaque fragment va calculer sa distance au point dans sa cellule ainsi qu'aux 8 cellules adjacentes et conserver la plus courte.
Le champ de distance final est visible dans l'exemple suivant :
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Explorez en essayant les choses suivantes :
* Redimensionner l'espace avec différentes valeurs.
* Pouvez vous penser à d'autres manières d'animer les points ?
* Comment pouvons ajouter un point supplémentaire comme la position de la souris ?
* Quelles autres manières de construire ce champ de distance (à part
m_dist = min(m_dist, dist);) vous viennent à l'esprit ?
* Imaginez quelques motifs à faire avec ce champ de distanes ?Cet algorithme peut également être interprété du point de vue des points au lieu des pixels.
Dans ce cas, on peut le décrire comme : chaque point grossit jusqu'à ce qu'il recouvre le rayon d'un autre point.
Ce qui reflète certaines règles de croissance de la nature, les formes de vie sont formées par la tension entre une force intérieure d'expansion et des forces contraires venues de l'extérieur.
L'algorithme canonique de ce genre de formes a été trouvé par Georgy Voronoi et porte maintenant son nom.
L'algorithme de Voronoi
Construire un diagramme de Voronoi à partir d'un bruit cellulaire est plus simple qu'il n'y paraît.
Il nous faut simplement conserver certaines informations en plus ; nous allons conerver le point le plus proche dans un
`vec2` appelé `m_point`.glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Notez que nous n'utilisons plus la fonction
`min` pour conserver la distance au point le plus proche mais que nous nous servons d'une condition `if`.
Pourquoi ? Parce que nous avons besoin de modifier la référence au point le plus proche (`m_point`) à chaque fois qu'un point est plus proche que le précédent (lignes 32 à 37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Notez également que la couleur de la cellule (branchée sur la position de la souris) change en fonction de sa position.
C'est parce que la couleur est déterminée par la valeur (la position) du point le plus proche.
Pour aller plus loin, nous pouvons à présent ré-utiliser l'algorithme de Steven Worley.
Essayez de l'implémenter seul, vous pouvez vous appuyer sur l'exemple suivant en cliquant dessus.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Après quoi, vous pouvez réfléchir à des utilisations créatives de cet algorithme.
Voici quelques pistes :
Améliorer Voronoi
En 2011, Stefan Gustavson a optimisé l'algorithme de Steven Worley sur le GPU en n'utilisant que des kernels de 2x2 au lieu de 3x3.
Cela amoindrit la charge de manière significative mais peut créer des artefacts et des discontinuités. Visibles dans l'exemple suivant:
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Plus tard, en 2012 Inigo Quilez a écrit un article expliquant comment trouver des frontières précises.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Inigio ne s'est pas arrété là et en 2014, il a écrit un article sur ce qu'il a appelé le voro-noise ; une exploration combinant bruit et cellules de Voronoi.
Je cite :
"Despite this similarity, the fact is that the way the grid is used in both patterns is different. Noise interpolates/averages random values (as in value noise) or gradients (as in gradient noise), while Voronoi computes the distance to the closest feature point. Now, smooth-bilinear interpolation and minimum evaluation are two very different operations, or... are they? Can they perhaps be combined in a more general metric? If that was so, then both Noise and Voronoi patterns could be seen as particular cases of a more general grid-based pattern generator?"
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Il est temps de revenir à la nature, d'être inspiré par elle et de trouver votre propre voie au travers de cette technique.
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
---
12/README Id (12/README-id.md)
Noise Seluler
Pada tahun 1996, enam belas tahun setelah Kebisingan asli Perlin dan lima tahun sebelum Kebisingan Simplex, Steven Worley menulis makalah berjudul "A Cellular Texture Basis Function". Di dalamnya, ia menjelaskan teknik tekstur prosedural yang sekarang banyak digunakan oleh komunitas grafis.
Untuk memahami prinsip di baliknya kita perlu mulai berpikir dalam iterasi. Mungkin Anda tahu apa artinya: ya, mulailah menggunakan loop
`untuk`. Hanya ada satu tangkapan dengan loop `untuk` di GLSL: angka yang kami periksa harus berupa konstanta (`const`). Jadi, tidak ada loop dinamis - jumlah iterasi harus diperbaiki.Mari kita lihat contohnya.
Poin untuk bidang jarak
Noise Seluler didasarkan pada bidang jarak, jarak ke yang terdekat dari serangkaian titik fitur. Katakanlah kita ingin membuat bidang jarak dari 4 titik. Apa yang harus kita lakukan? Nah, untuk setiap piksel kami ingin menghitung jarak ke titik terdekat. Itu berarti kita perlu mengulangi semua titik, menghitung jaraknya ke piksel saat ini dan menyimpan nilai untuk yang terdekat.
glslfloat min_dist = 100.; // A variable to store the closest distance to a point min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
Ini tidak terlalu elegan, tetapi berhasil. Sekarang mari kita implementasikan ulang menggunakan array dan loop
`for`.glslfloat m_dist = 100.; // minimum distance
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Perhatikan bagaimana kita menggunakan loop
`for` untuk mengulang melalui larik titik dan melacak jarak minimum menggunakan `min()` fungsi. Berikut adalah implementasi kerja singkat dari gagasan ini:<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
Dalam kode di atas, salah satu titik ditetapkan ke posisi mouse. Mainkan dengan itu sehingga Anda bisa mendapatkan ide intuitif tentang bagaimana kode ini berperilaku. Kemudian coba ini:
- Bagaimana Anda bisa menghidupkan poin lainnya?
- Setelah membaca bab tentang bentuk, bayangkan cara menarik menggunakan bidang jarak ini!
- Bagaimana jika Anda ingin menambahkan lebih banyak titik ke bidang jarak ini? Bagaimana jika kita ingin menambah / mengurangi poin secara dinamis?
Tile dan Iterasi
Anda mungkin memperhatikan bahwa loop
`for` dan array tidak terlalu berteman baik dengan GLSL. Seperti yang kami katakan sebelumnya, loop tidak menerima batas dinamis pada kondisi keluarnya. Selain itu, melakukan iterasi melalui banyak contoh akan mengurangi kinerja shader Anda secara signifikan. Itu berarti kita tidak dapat menggunakan pendekatan langsung ini untuk poin dalam jumlah besar. Kita perlu menemukan strategi lain, yang memanfaatkan arsitektur pemrosesan paralel dari GPU.Salah satu cara untuk mengatasi masalah ini adalah dengan membagi ruang menjadi tile. Tidak setiap piksel perlu memeriksa jarak ke setiap titik, bukan? Mengingat fakta bahwa setiap piksel berjalan di utasnya sendiri, kita dapat membagi ruang menjadi sel, masing-masing dengan satu titik unik untuk diperhatikan. Selain itu, untuk menghindari penyimpangan di tepi antar sel, kita perlu memeriksa jarak ke titik di sel tetangga. Itulah gagasan cemerlang utama dari makalah Steven Worley. Pada akhirnya, setiap piksel hanya perlu memeriksa sembilan posisi: titik selnya sendiri dan titik di 8 sel di sekitarnya. Kami sudah membagi ruang menjadi sel di bab-bab tentang: pola, acak dan noise, jadi semoga Anda sudah terbiasa dengan teknik ini sekarang.
glsl// Scale
st *= 3.; // Tile the space
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Jadi apa rencananya? Kami akan menggunakan koordinat petak (disimpan dalam koordinat bilangan bulat,
`i_st`) untuk membangun posisi acak suatu titik. Fungsi `random2f` yang akan kita gunakan menerima `vec2` dan memberi kita `vec2` dengan posisi acak. Jadi, untuk setiap tile kita akan memiliki satu titik fitur dalam posisi acak di dalam tile.glslvec2 point = random2(i_st);
Setiap piksel di dalam petak itu (disimpan dalam koordinat float,
`f_st`) akan memeriksa jaraknya ke titik acak itu.glslvec2 diff = point - f_st;
float dist = length(diff);
Hasilnya akan terlihat seperti ini:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Kami masih perlu memeriksa jarak ke titik-titik di tile sekitarnya, bukan hanya yang ada di tile saat ini. Untuk itu kita perlu iterasi melalui tile tetangga. Tidak semua tile, hanya yang tepat di sekitar tile saat ini. Artinya, dari petak
`-1` (kiri) ke `1` (kanan) dalam sumbu `x` dan `-1` (bottom) menjadi `1` (atas) dalam sumbu `y`. Wilayah 3x3 dengan 9 petak dapat diiterasi menggunakan pengulangan `for` ganda seperti ini:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Neighbor place in the grid
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Sekarang, kita dapat menghitung posisi titik pada masing-masing tetangga di loop ganda
`for` dengan menambahkan offset tile tetangga ke koordinat tile saat ini.glsl...
// Random position from current + neighbor place in the grid
vec2 point = random2(i_st + neighbor);
...
Selebihnya adalah tentang menghitung jarak ke titik itu dan menyimpan yang terdekat dalam variabel yang disebut
`m_dist` (untuk jarak minimum).glsl...
vec2 diff = neighbor + point - f_st; // Distance to the point
float dist = length(diff);
// Keep the closer distance
m_dist = min(m_dist, dist);
...
Kode di atas terinspirasi oleh artikel ini oleh Inigo's Quilez di mana dia berkata:
"... mungkin perlu diperhatikan bahwa ada trik bagus dalam kode ini di atas. Sebagian besar implementasi di luar sana mengalami masalah presisi, karena mereka menghasilkan titik acak mereka dalam ruang" domain "(seperti ruang "dunia" atau "objek "), yang dapat secara sewenang-wenang jauh dari asalnya. Seseorang dapat menyelesaikan masalah memindahkan semua kode ke tipe data presisi lebih tinggi, atau dengan menjadi sedikit pintar. Implementasi saya tidak menghasilkan poin dalam ruang "domain", tetapi di "sel "spasi: setelah bagian bilangan bulat dan pecahan dari titik bayangan diekstraksi dan oleh karena itu sel tempat kami bekerja diidentifikasi, yang kami pedulikan adalah apa yang terjadi di sekitar sel ini, yang berarti kami dapat membuang semua bagian bilangan bulat dari koordinat kami semua bersama-sama, menyimpan banyak bit presisi. Faktanya, dalam implementasi voronoi biasa, bagian bilangan bulat dari koordinat titik hanya meniadakan ketika titik fitur acak per sel dikurangi dari titik bayangan. Dalam penerapan di atas, kami bahkan tidak membiarkan tongkat itu lation terjadi, karena kita memindahkan semua komputasi ke ruang "sel". Trik ini juga memungkinkan seseorang untuk menangani kasus di mana Anda ingin voronoi-shade seluruh planet - seseorang dapat dengan mudah mengganti input menjadi presisi ganda, melakukan perhitungan floor() dan fract(), dan pergi floating point dengan sisa perhitungan tanpa membayar biaya untuk mengubah seluruh implementasi menjadi presisi ganda. Tentu saja, trik yang sama berlaku untuk pola Perlin Noise (tetapi saya belum pernah melihatnya diterapkan atau didokumentasikan di mana pun)."
Rekapitulasi: kami membagi ruang menjadi tile; setiap piksel akan menghitung jarak ke titik di tile mereka sendiri dan 8 tile sekitarnya; simpan jarak terdekat. Hasilnya adalah bidang jarak yang terlihat seperti contoh berikut:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Jelajahi lebih jauh dengan:
- Penskalaan ruang dengan nilai yang berbeda.
- Dapatkah Anda memikirkan cara lain untuk menghidupkan poin?
- Bagaimana jika kita ingin menghitung titik ekstra dengan posisi mouse?
- Cara lain apa untuk membangun bidang jarak ini yang dapat Anda bayangkan, selain
`m_dist=min(m_dist, dist);`?
- Pola menarik apa yang dapat Anda buat dengan bidang jarak ini?Algoritma ini juga dapat diinterpretasikan dari perspektif titik dan bukan piksel. Dalam hal itu dapat digambarkan sebagai: setiap titik tumbuh hingga menemukan area tumbuh dari titik lain. Ini mencerminkan beberapa aturan pertumbuhan di alam. Bentuk kehidupan dibentuk oleh ketegangan antara kekuatan dalam untuk berkembang dan tumbuh, dan batasan oleh kekuatan luar. Algoritme klasik yang menyimulasikan perilaku ini dinamai [Georgy Voronoi] (https://en.wikipedia.org/wiki/Georgy_Voronoy).
Algoritma Voronoi
Membuat diagram Voronoi dari kebisingan seluler tidak sesulit kelihatannya. Kami hanya perlu menyimpan beberapa informasi tambahan tentang titik persis yang paling dekat dengan piksel. Untuk itu kita akan menggunakan
`vec2` yang disebut `m_point`. Dengan menyimpan arah vektor ke pusat titik terdekat, bukan hanya jarak, kita akan "menyimpan" pengenal "unik" dari titik tersebut.glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Perhatikan bahwa dalam kode berikut ini kita tidak lagi menggunakan
`min` untuk menghitung jarak terdekat, tetapi pernyataan `if` biasa. Mengapa? Karena sebenarnya kita ingin melakukan sesuatu yang lebih setiap kali muncul titik dekat baru, yaitu menyimpan posisinya (baris 32 sampai 37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Perhatikan bagaimana warna sel yang bergerak (terikat pada posisi mouse) berubah warna sesuai dengan posisinya. Itu karena warna ditetapkan menggunakan nilai (posisi) titik terdekat.
Seperti yang kami lakukan sebelumnya, sekarang adalah waktu untuk meningkatkan ini, beralih ke pendekatan makalah Steven Worley. Coba terapkan sendiri. Anda dapat menggunakan bantuan contoh berikut dengan mengkliknya. Perhatikan bahwa pendekatan asli Steven Worley menggunakan sejumlah variabel poin fitur untuk setiap tile, lebih dari satu di sebagian besar tile. Dalam implementasi perangkat lunaknya di C, ini digunakan untuk mempercepat loop dengan membuat keluar lebih awal. Loop GLSL tidak mengizinkan jumlah variabel iterasi, jadi Anda mungkin ingin tetap menggunakan satu titik fitur per tile.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Setelah Anda mengetahui algoritme ini, pikirkan kegunaan yang menarik dan kreatif untuknya.
Meningkatkan Voronoi
Pada tahun 2011, Stefan Gustavson mengoptimalkan algoritme Steven Worley ke GPU dengan hanya melakukan iterasi melalui matriks 2x2 alih-alih 3x3. Ini mengurangi jumlah pekerjaan secara signifikan, tetapi dapat membuat artefak dalam bentuk diskontinuitas di tepi antara tile. Lihat contoh berikut.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Kemudian pada tahun 2012 Inigo Quilez menulis artikel tentang cara membuat perbatasan Voronoi yang tepat.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Eksperimen Inigo dengan Voronoi tidak berhenti sampai di situ. Pada tahun 2014 dia menulis artikel bagus ini tentang apa yang dia sebut voro-noise, sebuah fungsi yang memungkinkan perpaduan bertahap antara noise biasa dan voronoi . Dalam kata-katanya:
"Terlepas dari kesamaan ini, kenyataannya adalah bahwa cara grid digunakan di kedua pola berbeda. Noise interpolates/rata-rata nilai acak (seperti dalam noise nilai) atau gradien (seperti dalam noise gradien), sementara Voronoi menghitung jarak ke titik fitur terdekat. Sekarang, interpolasi halus-bilinear dan evaluasi minimum adalah dua operasi yang sangat berbeda, atau... benarkah? Bisakah keduanya digabungkan dalam metrik yang lebih umum? Jika demikian, pola Noise dan Voronoi bisa jadi dilihat sebagai kasus tertentu dari generator pola berbasis jaringan yang lebih umum?"
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Sekarang saatnya bagi Anda untuk melihat lebih dekat pada berbagai hal, terinspirasi oleh alam, dan menemukan cara Anda sendiri dalam menggunakan teknik ini!
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
---
12/README Pl (12/README-pl.md)
Cellular Noise (pol. "szum komórkowy")
W 1996 roku, szesnaście lat po oryginalnym algorytmie szumu Perlina i pięć lat przed jego Simplex Noise, Steven Worley napisał pracę zatytułowaną "A Cellular Texture Basis Function". Opisuje w niej technikę teksturowania proceduralnego, która jest obecnie szeroko stosowana przez grafików.
Aby zrozumieć jej zasady, musimy zacząć myśleć w kategoriach iteracji. Zapewne wiesz, co to oznacza: używanie pętli
for. Jest tylko jeden haczyk z pętlami for w GLSL: warunek, który sprawdzamy musi być stałą (const). Tak więc nie ma dynamicznych pętli - liczba iteracji musi być stała.Przyjrzyjmy się przykładowi.
Punkty w polu odległości
Cellular Noise opiera się na polach odległości, a dokładniej odległości do najbliższego ze zbioru punktów. Załóżmy, że chcemy stworzyć pole odległości składające się z 4 punktów. Co musimy zrobić? Cóż, dla każdego piksela chcemy obliczyć odległość do najbliższego punktu. Oznacza to, że musimy iterować po wszystkich punktach, obliczać ich odległości do bieżącego piksela i przechować odległość do tego najbliższego.
glslfloat min_dist = 100.; // Zmienna przechowująca odległość do najbliższego punktu min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
Nie jest to zbyt eleganckie, ale załatwia sprawę. Teraz zaimplementujmy go ponownie, używając tablicy i pętli
for.glslfloat m_dist = 100.; // minimum distance
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Zauważ, jak używamy pętli
for do iteracji po tablicy punktów i funkcji min() do śledzenia odległości do najbliższego punktu. Oto krótka działająca implementacja tego pomysłu:<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
W powyższym kodzie jeden z punktów jest przypisany do pozycji myszy. Pobaw się nim, abyś mógł zrozumieć intuicję stojącą za tym kodem. Następnie spróbuj ćwiczeń:
- Jak można animować pozostałe punkty?
- Po przeczytaniu rozdziału o kształtach, wyobraź sobie ciekawe sposoby wykorzystania tego pola odległości!
- Co, jeśli chcemy dodać więcej punktów do tego pola odległości? Co jeśli chcemy dynamicznie dodawać/odejmować punkty?
Kafelkowanie i iteracja
Zapewne zauważyłeś, że pętle
for i tablice nie są zbyt dobrymi przyjaciółmi GLSL. Jak już wspomnieliśmy, pętle nie akceptują dynamicznych warunków wyjścia. Ponadto, iteracja przez wiele instancji znacznie zmniejsza wydajność twojego shadera. Oznacza to, że nie możemy użyć tego prostego, brute-force'owego podejścia dla dużych ilości punktów. Musimy znaleźć inną strategię, taką, która wykorzystuje architekturę przetwarzania równoległego GPU.Jednym ze sposobów podejścia do tego problemu jest podzielenie przestrzeni na rozłączne obszary/komórki. Nie każdy piksel musi sprawdzać odległość do każdego punktu, prawda? Biorąc pod uwagę fakt, że każdy piksel działa w swoim własnym wątku, możemy podzielić przestrzeń na komórki, z których każda ma jeden unikalny punkt do oglądania. Ponadto, aby uniknąć aberracji na krawędziach między komórkami musimy sprawdzić odległości do punktów w sąsiednich komórkach. Jest to główna idea artykułu Stevena Worleya. Ostatecznie każdy piksel musi sprawdzić tylko dziewięć pozycji: punkt własnej komórki i punkty w 8 komórkach wokół niego. Dzieliliśmy już przestrzeń na komórki w rozdziałach o: wzorach kafelkowych, losowości i szumie, więc mam nadzieję, że jesteś już zaznajomiony z tą techniką.
glsl// skalowanie
st *= 3.; // dzielenie przestrzeni na kafelki
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Jaki jest więc plan? Użyjemy współrzędnych kafelka (przechowywanych w części całkowitej,
i_st) do skonstruowania losowej pozycji punktu. Funkcja random2f, której użyjemy, przyjmuje vec2 i zwraca vec2 z losową pozycją (o wartościach z zakresu od 0.0 do 1.0). Tak więc, dla każdego kafla będziemy mogli otrzymać jeden punkt losowo położony wewnątrz tego kafla.glslvec2 point = random2(i_st);
Każdy piksel wewnątrz tego kafla (przechowywany w części ułamkowej,
f_st) sprawdzi swoją odległość do tego nowego, losowego punktu.glslvec2 diff = point - f_st;
float dist = length(diff);
Wynik będzie wyglądał tak:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Musimy też sprawdzić odległości do punktów w okolicznych kafelkach, a nie tylko do tego w bieżącym kafelku. W tym celu iterujemy po sąsiednich kafelkach. Nie po wszystkich kafelkach, tylko po tych bezpośrednio otaczających bieżącego. Czyli od
-1 (lewy) do 1 (prawy) na osi x oraz od -1 (dolny) do 1 (górny) na osi y. Region 3x3 składający się z 9 kafelków może być iterowany przy użyciu podwójnej pętli for, jak poniżej:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Względne położenie sąsiadującego kafelka
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Teraz możemy obliczyć położenie punktów w każdym z sąsiadujących kafelków z pomocą podwójnej pętli
for, dodając przesunięcie sąsiedniego kafelka (neighbor) do współrzędnej bieżącego kafelka (i_st).glsl...
// Losowe położenie punktu wewnątrz sąsiedniego kafelka
vec2 point = random2(i_st + neighbor);
...
Reszta polega na obliczeniu odległości do każdego z sąsiednich punktów i zapisaniu najmniejszej odległości w zmiennej o nazwie
m_dist (z ang. "minimal distance").glsl...
vec2 diff = neighbor + point - f_st; // Odległość do punktu
float dist = length(diff);
// Zachowaj mniejszą odległość
m_dist = min(m_dist, dist);
...
Powyższy kod jest inspirowany artykułem Inigo Quileza, w którym zauważa, jak problemy z precyzją bitową mogą być uniknięte poprzez przejście do stosowanej już przez nas przestrzeni kafelkowej (w przeciwieństwie do robienia obliczeń w domyślnych współrzędnych, niepodzielonych na części całkowite i ułamkowe):
Podsumowując: dzielimy przestrzeń na kafelki; każdy piksel liczy odległość do punktu w swoim własnym kafelku i odległość do punktów z otaczających go 8 kafelków; przechowuje najbliższą odległość. Wynikiem jest pole odległości, które wygląda jak w poniższym przykładzie:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Eksploruj tę ideę dalej:
- Przeskaluj przestrzeń o różne wartości.
- Czy możesz wymyślić inne sposoby animacji punktów?
- Co jeśli chcemy obliczyć dodatkowy punkt z pozycji myszy?
- Jakie inne sposoby konstruowania tego pola odległości możesz sobie wyobrazić, poza
m_dist = min(m_dist, dist);?
- Jakie ciekawe wzory można stworzyć za pomocą tego pola odległości?Algorytm ten można również interpretować z perspektywy punktów, a nie bieżących pikseli. W takim przypadku można go opisać jako: każdy punkt rośnie, dopóki nie znajdzie rosnącego obszaru innego punktu. Odzwierciedla to niektóre z zasad wzrostu w naturze. Żywe formy kształtowane są przez napięcie między wewnętrzną siłą do rozszerzania się i wzrostu oraz zewnętrznymi siłami ograniczającymi. Klasyczny algorytm symulujący to zachowanie nazwany został za Georgy Voronoi.
Algorytm Voronoi
Konstruowanie diagramów Voronoi z szumu komórkowego jest mniej trudne niż mogłoby się wydawać. Musimy tylko zachować pewną dodatkową informację o punkcie, który jest najbliżej bieżącego piksela. Do tego celu użyjemy
vec2 o nazwie m_point (z ang. "minimal point"). Przechowując wektor od bieżącego piksela do najbliższego punktu (zamiast samej odległości) będziemy "przechowywać" "unikalny" identyfikator tego punktu.glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Zauważ, że w poniższym kodzie nie używamy już
min do obliczania najbliższej odległości, ale zwykłegpo warunku if. Dlaczego? Ponieważ chcemy zrobić coś więcej za każdym razem, gdy pojawi się nowy bliższy punkt, a mianowicie zapisać jego pozycję (linie 32 do 37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Zauważ, jak kolor ruchomej komórki (związanej z pozycją myszy) zmienia kolor w zależności od jej położenia. To dlatego, że kolor jest przypisywany przy użyciu wartości (pozycji) najbliższego punktu.
Podnieśmy poprzeczkę, przechodząc na podejście z artykułu Stevena Worleya. Spróbuj zaimplementować to samodzielnie. Możesz skorzystać z pomocy poniższego przykładu, klikając na niego. Zauważ, że oryginalne podejście Stevena Worleya używa zmiennej liczby punktów dla każdego kafla, więcej niż jeden w większości kafli. W tej jego nie-shaderowej implementacji (bo w C, a nie w GLSL) pomaga to przyspieszyć pętlę poprzez wczesne jej opuszczanie. Pętle GLSL nie pozwalają na zmienną liczbę iteracji, więc prawdopodobnie chcesz trzymać się jednego punktu na kafelek.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Gdy już rozgryziesz ten algorytm, pomyśl o ciekawych i kreatywnych jego zastosowaniach.
Ulepszenie Voronoi
W 2011 roku Stefan Gustavson zoptymalizował algorytm Stevena Worleya pod GPU, iterując tylko przez macierz 2x2 zamiast 3x3. To znacznie zmniejsza ilość pracy, ale może tworzyć artefakty w postaci nieciągłości na krawędziach między kafelkami. Przyjrzyj się poniższym przykładom.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Później w 2012 roku Inigo Quilez napisał artykuł o tym, jak zrobić Voronoi z ostrymi granicami.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Eksperymenty Inigo z Voronoi nie skończyły się na tym. W 2014 roku napisał artykuł o tym, co nazywa voro-noise. Jest to funkcja, która pozwala na stopniowe interpolowanie między zwykłym szumem a Voronoi. Jego słowami:
"Pomimo tego podobieństwa, faktem jest, że sposób użycia kafelkowania w obu metodach jest inny. Szum interpoluje/uśrednia wartości losowe (jak w Value Noise) lub gradienty (jak w Gradient Noise), podczas gdy Voronoi oblicza odległość do najbliższego punktu w kafelku. Interpolacja dwuliniowa (ang. "bilinear") i wartość minimalna to dwie bardzo różne operacje, ale czy na pewno? Czy można je połączyć w bardziej ogólną metrykę? Gdyby tak było, to zarówno szum jak i Voronoi mogłyby być postrzegane jako szczególne przypadki bardziej ogólnego generatora wzorów kafelkowych?".
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Teraz nadszedł czas, abyś przyjrzał się bliżej rzeczom, zainspirował się naturą i znalazł swoje własne ujęcie tej techniki!
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
---
12/README Pt (12/README-pt.md)
Cellular Noise (Ruído Celular)
Em 1996, dezesseis anos após o Noise original de Perlin e cinco anos antes de seu Noise Simplex, Steven Worley escreveu um artigo chamado “A Cellular Texture Basis Function” (Uma Função de Base de Textura Celular). Neste, ele descreve uma técnica procedimental de texturizar amplamente usada pela comunidade gráfica.
Para entender os princípios por trás disto, nós precisamos começar a pensar em termos de iterações. Provavelmente, você sabe o que isso significa: sim, começar a usar laços de repetição
`for`. Há apenas uma questão com laços de repetição `for` em GLSL: o número de vezes que estamos verificando deve ser uma constante (`const`). Logo, sem laços dinâmicos - o número de iterações deve ser fixo.Vamos dar uma olhada em um exemplo.
Pontos para um campo de distância
Ruídos celulares são baseados em campos de distância, a distância para o ponto mais próximo de um conjunto de pontos. Vamos dizer que nós queremos criar um campo de distância de 4 pontos. O que nós devemos fazer? Bem, para cada píxel, nós queremos calcular a distância para o ponto mais próximo. Que significa que nós precisamos iterar por todos os pontos, computar suas distâncias para o píxel atual e armazenar o valor para o mais próximo.
glslfloat min_dist = 100.; // A variable to store the closest distance to a point min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
Isso não é muito elegante, mas resolve. Agora, vamos reimplementá-lo usando um array e um laço
`for`.glslfloat m_dist = 100.; // minimum distance
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Veja como nós usamos o laço
`for` para iterar pelo array de pontos e encontrar a menor distância usando uma função `min()`. Aqui está uma breve implementação funcional desta ideia:<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
No código acima, um ou mais pontos estão atribuídos à posição do mouse. Interaja com ele para pegar uma ideia mais intuitiva de como o código se comporta. Então, responda:
- Como você pode animar o resto dos pontos?
- Depois de ler o capítulo sobre formas, imagine formas interessantes de usar este campo de distância!
-E se você quiser adicionar mais pontos ao campo de distância? E se nós quisermos dinamicamente adicionar/subtrair pontos?
Tiles e Iteração
Você provavelmente percebeu que laços
`for` e arrays não se dão bem com GLSL. Como mencionamos antes, laços não aceitam limites dinâmicos em sua condição de saída. Além disso, iterar por muitas instâncias, reduz a performance do shader significativamente. O que significa que nós não podemos usar essa abordagem diretamente para grandes quantidades de pontos. Nós precisamos encontrar outra estratégia, uma que tira vantagem sobre o arquitetura de processamento paralelo da GPU.Uma forma de abordar este problema é dividindo o espaço em tiles. Cada píxel não precisa checar a distância de cada ponto, certo? Considerando o fato de cada píxel rodar em sua própria thread, nós podemos subdividir o espaço em células, cada uma com um único ponto para observar. Também, para evitar artefatos nas arestas entre as células, nós precisamos verificar as distâncias para os pontos das células vizinhas. Esta é a genial ideia do artigo de Steven Worley. No final, cada píxel precisa verificar apenas nove posições: o ponto da própria célula e os pontos das 8 células ao seu redor. Nós já subdividimos o espaço em células no capítulo sobre [padrões], (../09/), aleatório e noise, então você provavelmente já está familiarizado com essa técnica.
glsl// Scale
st *= 3.; // Tile the space
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Então, qual é o plano? Nós usaremos as coordenadas do tile (armazenadas na coordenada inteira
`i_st`) para construirmos uma posição aleatória de um ponto. A função `random2f` que nós usaremos recebe um `vec2` e nos retorna um `vec2` com uma posição aleatória. Então, para cada tile, nós teremos um ponto respectivo em uma posição aleatória dentro do tile.glslvec2 point = random2(i_st);
Cada píxel dentro do tile (armazenado em coordenadas float,
`f_st`) verificará sua distância para um ponto aleatório.glslvec2 diff = point - f_st;
float dist = length(diff);
O resultado se parecerá com a seguinte imagem:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Ainda precisamos verificar a distância dos pontos nos tiles ao redor, não apenas o atual. Para isso, nós precisamos iterar por seus tiles vizinhos. Não todos os tiles, apenas os que estão imediatamente ao redor do atual. Que compreende do tile
`-1` (esquerda) a `1` (direita) no eixo `x` e `-1`(inferior) e `1` (superior) no eixo `y`. Podemos iterar em uma região 3x3 de 9 tiles através de um laço `for` duplo como esse:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Neighbor place in the grid
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Agora, nós podemos estimar a distância dos pontos para cada um dos vizinhos em nosso laço
`for` duplo, ao adicionar o deslocamento do tile vizinho às coordenadas do tile atual.glsl...
// Random position from current + neighbor place in the grid
vec2 point = random2(i_st + neighbor);
...
O resto é calcular a distância daquele ponto e armazenar a menor em uma variável chamada
`m_dist` (para distância mínima).glsl...
vec2 diff = neighbor + point - f_st; // Distance to the point
float dist = length(diff);
// Keep the closer distance
m_dist = min(m_dist, dist);
...
O código acima foi inspirado por este artigo de Inigo Quilez onde ele diz:
"... Pode valer a pena perceber que existe um truque excelente no código acima. A maioria das implementações por aí sofrem com problemas de precisão, porque eles geram seus pontos aleatórios no espaço "domínio" (como espaço "mundo" ou espaço "objeto"), que podem ser longe da origem. Pode-se resolver este problema ao aumentar a precisão dos dados, ou sendo um pouco esperto. Minha implementação não gera os pontos em um espaço "domínio", mas em espaço "célula": uma vez que as partes inteira e fracionária do ponto desejado são extraídas e a célula em que estamos trabalhando, identificada, todos nós nos importamos com o que acontece ao redor desta célula, assim nós podemos nos livrar de todas as partes inteiras de uma vez, nós economizando bits de precisão. De fato, em uma implementação de voronoi usual, as partes inteiras das coordenadas do ponto simplesmente se cancelam quando o aleatório pelos pontos da respectiva célula são subtraídos do ponto em questão. Em minha implementação acima, nós nem ao menos deixamos esse cancelamento acontecer, pois estamos movendo todos os cálculos para o espaço "célula". Estudo truque também permite que lidemos com os casos nos quais queremos usar voronoi para desenhar um planeta inteiro - poderíamos simplesmente substituir a entra para ser precisão double, realizar os cálculos de floor() e fract(), e então mudarmos para ponto-flutuante para os próximos cálculos sem pagarmos o custo de trocar toda implementação para precisão double. Naturalmente, alguns truques se aplicam ao ruído Perlin (ainda que eu não tenha visto isto documentado ou implementado)."
Recapitulando: nós subdividimos o espaço em tiles; cada píxel calculará a distância do ponto do seu próprio tile e dos 8 ao seu redor; armazenará a menor distância. O resultado será um campo de distância que se parece com o exemplo a seguir:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Vá além ao:
- Transformar a escala no espaço para diferentes valores.
- Você consegue pensar em outras formas de animar os pontos?
- E se quisermos calcular um ponto extra com a posição do mouse?
- Quais são as outras formas de construir este campo de distância, você consegue pensar em uma além de
`m_dist = min(m_dist, dist);`?
- Que padrões interessantes você pode criar com este campo de distância?O algoritmo pode também ser interpretado pela perspectiva do ponto e não dos píxeis. Neste caso, ele pode ser descrito como: cada ponto cresce até que ele encontre a área de crescimento de outro ponto. Isso reflete algumas leis de crescimento na natureza. Formas de vida são moldadas por essa tensão entre a força interior de crescer e se expandir e as limitações das forças externas. O clássico algoritmo que simula este comportamento foi nomeado em homenagem a Georgy Voronoi.
O Algoritmo de Vonoroi
Construindo diagramas a partir de ruído celular é menos complicado do que parece. Nós apenas precisamos manter algumas informações extras sobre o ponto que é o mais próximo de cada píxel. Para isso, nós vamos usar um
`vec2` chamado `m_point`. Ao armazenar a direção do vetor até o centro do ponto mais próximo, ao invés de apenas a distância, nós "manteremos" um "único" identificador para aquele ponto.glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Veja que no código a seguir que não estamos mais usando
`min` para calcular a distância do ponto mais próximo, mas um `if`. Por quê? Porque nós queremos fazer algo toda a vez que um ponto mais próximo aparecer, armazenar sua posição (linhas 32 a 37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Veja como a cor do movimento celular (vinculada à posição do mouse) muda sua cor de acordo com a sua posição. Isso acontece porque a cor é atribuída usando o valor (posição) do ponto mais próximo.
Como nós fizemos antes, agora é hora de ir além, mudar para a abordagem do artigo de Steven Worley](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Tente implementá-lo sozinho. Você pode usar a ajuda dos seguintes exemplos clicando neles. Veja que a abordagem original de Steven Worley usa um número variável de pontos para cada tile, mais de um em sua maioria. Em sua implementação em C, isso é usado para acelerar o laço ao fazer uma saída antecipada. Laços em GLSL não permitem um número variavel de iterações, então você provavelmente vai querer manter um ponto por tile.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Depois que você pensar neste algoritmo, pense em seus vários usos interessantes e criativos.
Melhorando Voronoi
Em 2011, Stefan Gustavson otimizou o algoritmo de Steven Worley para GPU ao iterar por uma matriz de 2x2 ao invés de 3x3. Isso reduz a quantidade de trabalho significativamente, mas pode criar artefatos em forma de descontinuidades nas arestas entre os tiles. Dê uma olhada no seguinte exemplo.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Mais tarde em 2012 Inigo Quilez escreveu um artigo sobre como criar bordas mais precisas em Voronoi.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Os experimentos de Inigo com Voronoi não pararam aí. Em 2014, ele escreveu um excelente artigo sobre o que ele chama voro-noise, uma função que permite uma mesclagem gradual entre o ruído comum e voronoi. Em suas palavras:
"Apesar de sua similaridade, o fato é que a forma que a grade é usada em ambos padrões é diferente. O ruído interpola valores aleatórios (como ruído de valor) ou gradiente (como em ruído de gradiente), enquanto Voronoi calcula a distância de um respectivo ponto. Agora, a interpolação bilinear e a verificação da menor distância são duas operações diferentes, ou.... elas são? Poderiam elas serem combinadas em uma métrica mais geral? Se sim, então tanto Ruído como Voronoi podem ser vistos como casos particulares de um gerador de padrão baseado em grade mais geral?"
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Agora, é hora de você dar uma olhada nas coisas, se inspire pela natureza e encontre a sua própria forma de usar esta técnica!
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
---
12/README Tr (12/README-tr.md)
Hücresel Gürültü
1996'da, Perlin'in orijinal Gürültüsünden on altı yıl sonra ve Simpleks Gürültüsünden beş yıl önce, Steven Worley "A Cellular Texture Basis Function" adlı bir makale yazdı. Bu makalede, grafik topluluğu tarafından artık yaygın olarak kullanılan prosedürel bir dokulama tekniği tanımladı.
Arkasındaki prensipleri anlamak için yineleme açısından düşünmeye başlamamız gerekir. Muhtemelen bunun ne anlama geldiğini biliyorsunuzdur: evet,
`for` döngülerini kullanmaya başlayın. GLSL'de `for` döngülerinin tek bir tuhaflığı var: kontrol ettiğimiz sayı sabit (`const`) olmalıdır. Yani dinamik döngü yok — yineleme sayısı sabitlenmelidir.Bir örneğe bakalım.
Mesafe alanı için noktalar
Hücresel Gürültü, mesafe alanlarına dayanır; bir dizi özellik noktasından en yakın olanına olan mesafe. Diyelim ki 4 noktanın mesafe alanını yapmak istiyoruz. Ne yapmamız gerekir? Her piksel için en yakın noktaya olan mesafeyi hesaplamak istiyoruz. Bu, tüm noktalar arasında yinelememiz, mevcut piksele olan mesafelerini hesaplamamız ve en yakın olanın değerini saklamamız gerektiği anlamına gelir.
glslfloat min_dist = 100.; // Bir noktaya en yakın mesafeyi saklayan değişken min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
Bu çok zarif değildir, ama işi görür. Şimdi bir dizi ve
`for` döngüsü kullanarak yeniden uygulayalım.glslfloat m_dist = 100.; // minimum mesafe
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
`min()` fonksiyonu kullanarak minimum mesafeyi takip etmek için `for` döngüsünü nasıl kullandığımıza dikkat edin. İşte bu fikrin kısa bir çalışan uygulaması:<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
Yukarıdaki kodda noktalardan biri fare konumuna atanmıştır. Bu kodun nasıl davrandığı hakkında sezgisel bir fikir edinmek için onunla oynayın. Sonra şunu deneyin:
- Geri kalan noktaları nasıl canlandırabilirsiniz?
- Şekiller bölümünü okuduktan sonra, bu mesafe alanını kullanmanın ilginç yollarını hayal edin!
- Ya bu mesafe alanına daha fazla nokta eklemek isterseniz? Ya dinamik olarak nokta eklemek/çıkarmak istesek?
Döşeleme ve yineleme
Muhtemelen
`for` döngülerinin ve dizilerin GLSL ile çok iyi arkadaş olmadığını fark etmişsinizdir. Daha önce söylediğimiz gibi, döngüler çıkış koşullarında dinamik sınırlar kabul etmez. Ayrıca birçok örnek arasında yineleme yapmak shader'ınızın performansını önemli ölçüde azaltır. Bu, büyük miktarda nokta için bu doğrudan yaklaşımı kullanamayacağımız anlamına gelir. GPU'nun paralel işleme mimarisinden yararlanan başka bir strateji bulmamız gerekir.Bu soruna yaklaşmanın bir yolu, uzayı döşemelere bölmektir. Her pikselin her bir noktaya olan mesafeyi kontrol etmesi gerekmiyor, değil mi? Her piksel kendi iş parçacığında çalıştığı göz önüne alındığında, uzayı hücrelere bölebiliriz; her biri izlenecek benzersiz bir noktaya sahiptir. Ayrıca, hücreler arasındaki kenarlardaki sapmaları önlemek için komşu hücrelerdeki noktalara olan mesafeleri de kontrol etmemiz gerekir. Steven Worley'nin makalesinin ana parlak fikri budur. Sonuçta, her pikselin yalnızca dokuz konumu kontrol etmesi gerekir: kendi hücresinin noktası ve etrafındaki 8 hücredeki noktalar. Uzayı zaten şu bölümlerde hücrelere ayırdık: desenler, rastgelelik ve gürültü, bu yüzden umarız bu tekniğe artık aşinasınızdır.
glsl// Ölçekleme
st *= 3.; // Uzayı döşeleme
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Plan nedir? Döşeme koordinatlarını (tam sayı koordinatında saklanan,
`i_st`) bir noktanın rastgele konumunu oluşturmak için kullanacağız. Kullanacağımız `random2f` fonksiyonu bir `vec2` alır ve bize rastgele bir konuma sahip bir `vec2` verir. Böylece her döşeme için döşemenin içinde rastgele bir konumda bir özellik noktası olacaktır.glslvec2 point = random2(i_st);
O döşemenin içindeki her piksel (float koordinatında saklanan,
`f_st`) o rastgele noktaya olan mesafesini kontrol edecektir.glslvec2 diff = point - f_st;
float dist = length(diff);
Sonuç şöyle görünecektir:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Yine de sadece mevcut döşemedeki değil, çevreleyen döşemelerdeki noktalara olan mesafeleri de kontrol etmemiz gerekir. Bunun için komşu döşemeler arasında yinelememiz gerekir. Tüm döşemeler değil, sadece mevcut olanın hemen çevresindekiler. Yani
`x` ekseninde `-1` (sol) ile `1` (sağ) döşeme ve `y` ekseninde `-1` (alt) ile `1` (üst) arasında. 9 döşemelik 3x3'lük bir bölge, şöyle bir çift `for` döngüsüyle yinelenebilir:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Izgaradaki komşu yeri
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Şimdi, çift
`for` döngümüzde komşu döşeme ofsetini mevcut döşeme koordinatına ekleyerek komşuların her birindeki noktaların konumunu hesaplayabiliriz.glsl...
// Mevcut + komşu ızgara yerinden rastgele konum
vec2 point = random2(i_st + neighbor);
...
Gerisinin tamamı, o noktaya olan mesafeyi hesaplama ve en yakın olanı
`m_dist` (minimum mesafe) adlı bir değişkende saklama ile ilgilidir.glsl...
vec2 diff = neighbor + point - f_st; // Noktaya olan mesafe
float dist = length(diff);
// Daha yakın mesafeyi tut
m_dist = min(m_dist, dist);
...
Yukarıdaki kod, Inigo Quilez'in bu makalesinden ilham almıştır.
Özetleyecek olursak: uzayı döşemelere ayırırız; her piksel kendi döşemesindeki ve çevresindeki 8 döşemedeki noktaya olan mesafeyi hesaplar; en yakın mesafeyi saklar. Sonuç, aşağıdaki örneğe benzeyen bir mesafe alanıdır:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Daha fazla keşfedin:
- Uzayı farklı değerlerle ölçekleyin.
- Noktaları canlandırmanın başka yollarını düşünebilir misiniz?
- Ya fare konumuyla ekstra bir nokta hesaplamak istesek?
-
`m_dist = min(m_dist, dist);` dışında bu mesafe alanını oluşturmanın hangi diğer yollarını hayal edebilirsiniz?
- Bu mesafe alanıyla hangi ilginç desenleri yapabilirsiniz?Bu algoritma, piksellerin değil noktaların perspektifinden de yorumlanabilir. Bu durumda şöyle tarif edilebilir: her nokta, başka bir noktanın büyüyen alanını bulana kadar büyür. Bu, doğadaki bazı büyüme kurallarını yansıtır. Yaşayan formlar, genişleme ve büyüme iç gücü ile dış güçlerin sınırlamaları arasındaki bu gerilim tarafından şekillendirilir. Bu davranışı simüle eden klasik algoritma, Georgy Voronoi adını taşır.
Voronoi Algoritması
Hücresel gürültüden Voronoi diyagramları oluşturmak göründüğünden daha az zordur. Sadece piksele en yakın olan kesin nokta hakkında bazı ek bilgileri saklamamız gerekir. Bunun için
`m_point` adlı bir `vec2` kullanacağız. En yakın noktanın merkezine olan vektör yönünü sadece mesafe yerine saklayarak, o noktanın "benzersiz" bir tanımlayıcısını "saklayacağız".glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Aşağıdaki kodda en yakın mesafeyi hesaplamak için artık
`min` kullanmadığımızı, normal bir `if` ifadesi kullandığımızı not edin. Neden? Çünkü daha yakın yeni bir nokta göründüğünde aslında daha fazlasını yapmak, yani konumunu saklamak (32-37 arası satırlar) istiyoruz.<div class="codeAndCanvas" data="vorono-00.frag"></div>
Hareket eden hücrenin (fare konumuna bağlı) renginin konumuna göre nasıl değiştiğine dikkat edin. Bu, rengin en yakın noktanın değeri (konumu) kullanılarak atanmasından kaynaklanır.
Daha önce yaptığımız gibi, şimdi bunu ölçeklendirmenin, Steven Worley'nin makalesinin yaklaşımına geçmenin zamanı. Kendiniz uygulamayı deneyin. Tıklayarak aşağıdaki örneğin yardımını alabilirsiniz.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Bu algoritmayı anladıktan sonra, ilginç ve yaratıcı kullanımlar düşünün.
Voronoi'yi İyileştirme
2011'de, Stefan Gustavson Steven Worley'nin algoritmasını GPU için optimize etti, 3x3 yerine sadece 2x2 matris üzerinde yineleme yaparak. Bu, iş miktarını önemli ölçüde azaltır, ancak döşemeler arasındaki kenarlarda süreksizlik şeklinde artefaktlar oluşturabilir. Aşağıdaki örneklere bakın.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Daha sonra 2012'de Inigo Quilez kesin Voronoi sınırları yapma hakkında bir makale yazdı.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Inigo'nun Voronoi deneyleri burada bitmedi. 2014'te voro-noise dediği şey hakkında güzel bir makale yazdı, normal gürültü ile voronoi arasında kademeli bir harmanlama sağlayan bir fonksiyon. Kendi sözleriyle:
"Bu benzerliğe rağmen, gerçek şu ki ızgaranın her iki desende kullanılma şekli farklıdır. Gürültü rastgele değerleri (değer gürültüsünde olduğu gibi) veya gradyanları (gradyan gürültüsünde olduğu gibi) enterpolasyon yapar/ortalar, Voronoi ise en yakın özellik noktasına olan mesafeyi hesaplar. Şimdi, düzgün çift doğrusal enterpolasyon ve minimum değerlendirme çok farklı iki işlemdir, veya... öyle midir? Belki daha genel bir metrikte birleştirilebilirler mi? Eğer öyleyse, hem Gürültü hem de Voronoi desenleri, daha genel bir ızgara tabanlı desen oluşturucunun özel durumları olarak görülebilir mi?"
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Şimdi yakından bakma, doğadan ilham alma ve bu tekniğe kendi yaklaşımınızı bulma zamanı!
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
#### Araç kutunuz için
* LYGIA'nın üretken fonksiyonları, GLSL'de desenler üretmek için yeniden kullanılabilir fonksiyonlar kümesidir. Üretken sanat yaratmak için rastgelelik ve gürültüyü nasıl kullanacağınızı öğrenmek için harika bir kaynaktır. Yeniden kullanılabilirlik, performans ve esneklik için tasarlanmış çok ayrıntılı bir kütüphanedir. Ve herhangi bir projeye ve framework'e kolayca eklenebilir.
---
12/README Ua (12/README-ua.md)
Клітинний шум
У 1996 році, через шістнадцять років після оригінального шуму Перліна та за п’ять років до його симплексного шуму, Steven Worley написав статтю під назвою “Базова функція клітинної текстури”. У ній він описує техніку процедурного текстурування, яка зараз широко використовується графічною спільнотою.
Щоб зрозуміти принципи, що лежать в її основі, нам потрібно почати думати в термінах ітерацій. Напевно, ви розумієте, що це означає використання циклів
`for`. У циклах `for` в GLSL є лише одна заковика: число, яке використовується у перевірці лічильника, має бути константою (`const`). Отже, ніяких динамічних циклів — кількість ітерацій має бути фіксованою.Давайте розглянемо приклад.
Точки у полі відстаней
Клітинний шум базується на полях відстаней, що розташовані до найближчої точки з певного набору. Скажімо, ми хочемо створити поле відстаней з 4 точок. Що нам потрібно зробити? Для кожного пікселя ми хочемо обчислити відстань до найближчої точки. Це означає, що нам потрібно виконати ітерацію по всім точкам, обчислити до них відстані від поточного пікселя та зберегти значення до найближчої.
glslfloat min_dist = 100.; // змінна для збереження відстані до найближчої точки min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
Не дуже елегантно, але робить свою справу. Тепер реалізуємо цей підхід за допомогою масиву та циклу
`for`:glslfloat m_dist = 100.; // мінімальна відстань for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Зверніть увагу, як ми використовуємо цикл
`for`, щоб перебирати масив точок і відстежувати мінімальну відстань за допомогою функції `min()`. Ось коротка робоча реалізація цієї ідеї:<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
У наведеному вище коді одна з точок прив'язана до положення курсору. Пограйте з прикладом, щоб отримати краще інтуїтивне уявлення щодо поведінки програми. Потім спробуйте наступне:
- Як анімувати решту точок?
- Прочитавши розділ про фігури, уявіть цікаві способи використання з цим полем відстаней!
- Як додати більше точок до цього поля відстаней? Що можна зробити для динамічного додавання або видалення точок?
Замощення та ітерація
Ви, мабуть, помітили, що цикли
`for` та масиви не дуже дружать з GLSL. Як ми вже говорили раніше, місцеві цикли не здатні на динамічну кількість ітерацій. Крім того, велика кількість ітерацій значно знижує продуктивність вашого шейдера. Це означає, що ми не можемо використовувати цей прямолінійний підхід для великої кількості точок. Нам потрібно знайти іншу стратегію, яка буде використовувати переваги архітектури GPU з її паралельними обчисленнями.Один з підходів до розв'язання цієї проблеми — це розділення простору на плитки. Кожному пікселю не обов'язково перевіряти відстань до кожної окремої точки, чи не так? Враховуючи той факт, що кожен піксель працює у власному потоці, ми можемо розділити простір на комірки, кожна з яких матиме одну унікальну точку для спостереження. Крім того, щоб уникнути аберацій на межах між комірками, нам потрібно перевірити відстань до точок лише у сусідніх клітинах. Це і є головною чудовою ідеєю у статті Steven Worley. Зрештою, кожен піксель має перевірити лише дев’ять позицій: точку своєї клітини та точки у 8 клітинах навколо неї. Ми вже поділяли простір на комірки в розділах про: патерни, випадковість та шум, тож, сподіваюся, ви вже знайомі з цією технікою.
glsl// Масштаб
st *= 3.; // Розділ простору на плитки
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Отже, який план? Ми будемо використовувати координати клітини-плитки, збережені в цілочисельній координаті
`i_st`, щоб побудувати випадкову позицію точки. Функція `random2f`, яку ми будемо використовувати, отримує на вхід `vec2` та повертає нам також `vec2` з випадковою для даної клітини координатою. Таким чином, для кожної плитки ми матимемо одну особливу точку у випадковому місці цієї плитки.glslvec2 point = random2(i_st);
Кожен піксель усередині плитки, з координатою збереженою у змінній
`f_st`, перевірятиме свою відстань до випадкової точки, про яку ми говорили раніше.glslvec2 diff = point - f_st;
float dist = length(diff);
Результат буде виглядати так:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Нам все ще потрібно перевірити відстані до точок у навколишніх клітинах, а не лише на поточній. Для цього нам потрібно ітерувати сусідні клітини. Не всі клітини, а лише ті, що знаходяться безпосередньо навколо поточної. Тобто від
`-1` клітини ліворуч, до `+1` клітини праворуч по осі `x` та від `-1` клітини знизу до `+1` клітини зверху по осі `y`. Цю ділянку розміром 3x3 із 9 клітин можна обійти за допомогою подвійного циклу `for`:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Сусідня позиція клітини у сітці
vec2 neighbor = vec2(float(x), float(y));
...
}
}
Тепер у вкладеному циклі
`for` ми можемо обчислити положення наших випадкових точок у кожній сусідній клітині, додаючи зміщення сусідньої клітини до координат поточної.glsl...
// Положення випадкової точки у сусідній плитці
vec2 point = random2(i_st + neighbor);
...
Решта зводиться до обчислення відстаней до цієї точки та збереження найближчої у змінну
`m_dist`.glsl...
vec2 diff = neighbor + point - f_st; // Відстань до точки
float dist = length(diff);
// Збереження ближчої дистанції
m_dist = min(m_dist, dist);
...
Наведений вище код створено на основі статті Inigo's Quilez, де він писав:
"... варто зазначити, що у наведеному вище коді є гарний трюк. Більшість реалізацій страждають від проблем із точністю, оскільки вони генерують свої випадкові точки в просторі "домену" (наприклад, у "глобальному" просторі чи просторі "об'єкта"), який може бути як завгодно далеко від початку координат. Проблему можна вирішити, перемістивши весь код до типів даних з вищою точністю, або проявивши трохи кмітливості. Моя реалізація генерує точки не в просторі "домену", а в просторі "комірки": як тільки із фрагментного пікселя отримано цілу та дробову частини, а значить визначено клітину, в якій ми працюємо, нас цікавитиме лише те, що відбувається навколо цієї клітини, а не весь простір. Тобто ми можемо відкинути цілочисельну частину значення наших координат та зберегти кілька біт точності. Насправді у звичайній реалізації діаграми Вороного, цілі частини координат точки просто скасовуються, коли випадкові точки клітини віднімаються від поточної шейдерної точки. У реалізації вище ми не робимо цього, оскільки переносимо всі обчислення в простір "комірки". Цей підхід дозволяє накласти діаграму Вороного хоч на цілу планету — достатньо просто подвоїти точність для вхідних даних, виконати для них обчислення з floor() і fract(), а решту обчислень проводити вже зі звичайною точністю для float, не витрачаючи обчислювальні ресурси на зміну всієї реалізації до подвійної точності. Звісно, той самий трюк можна застосувати й до підходів з шумом Перліна, але я ніколи не бачив подібну реалізацію чи опис подібного алгоритму)."
Підсумовуючи: розбиваємо простір на клітини. Кожен піксель обчислює відстань до точки у власній клітині та навколишніх 8 клітинах, зберігаючи найближчу відстань. В результаті маємо поле відстаней, що виглядає як на прикладі нижче:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Дослідіть цей приклад глибше:
- Масштабуйте простір різними значеннями.
- Вигадайте інші способи анімації точок.
- Що потрібно зробити для обчислення додаткової точки у положенні курсору?
- Які інші способи побудови цього поля відстаней ви можете собі уявити, окрім
`m_dist = min(m_dist, dist);`?
- Які цікаві візерунки можна зробити за допомогою цього поля відстаней?Цей алгоритм також можна інтерпретувати з точки зору точок, а не пікселів. У цьому випадку його можна описати таким чином: кожна точка росте, доки не натрапить на область росту іншої точки. Ця поведінка відображає деякі правила росту в природі. Живі організми формуються через цю напругу між внутрішньою силою зростання й розширення та обмеженнями зовнішніх сил. Класичний алгоритм, який імітує таку поведінку, названий на честь Георгія Вороного.
Алгоритм Вороного
Побудувати діаграми Вороного з клітинного шуму не так складно, як може здатися. Нам просто потрібно зберегти додаткову інформацію про найближчу точку до пікселя. Для цього ми використаємо змінну
`m_point` типу `vec2`. Зберігаючи вектор напрямку до найближчої точки замість відстані, ми "отримаємо" "унікальний" ідентифікатор цієї точки.glsl...
if ( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Зауважте, що в наступному коді, для обчислення найближчої відстані ми використовуємо звичайний оператор
`if` замість функції `min`. Чому? Тому що, при знаходженні нової найближчої точки, ми хочемо зробити додаткову дію і зберегти її координати (рядки 32-37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Зверніть увагу, як колір рухомої комірки, прив'язаної до положення курсору, змінює колір відповідно до його положення. Це тому, що колір визначається залежно від позиції найближчої точки.
Як і у попередніх прикладах, настав час збільшити масштаби, скориставшись підходом зі статті за авторства Steven Worley. Спробуйте реалізувати його самостійно. Ви можете скористатися допомогою наступного прикладу, клацнувши на нього. Зауважте, що оригінальний підхід від Steven Worley використовує змінну кількість точок для кожної клітини, більш ніж одну у більшості випадків. У його програмній реалізації на мові C це використовується для прискорення циклу завдяки раннім виходам із нього. Цикли GLSL не дозволяють змінювати кількість ітерацій, тому вам, ймовірно, доведеться використовувати лише одну точку на клітину.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Розібравшись із цим алгоритмом, подумайте про цікаві та креативні способи його використання.
Поліпшення діаграми Вороного
У 2011 році Stefan Gustavson оптимізував алгоритм Steven Worley для GPU, обходячи лише матрицю 2x2 замість 3x3. Це значно зменшує обсяг роботи, але може створювати артефакти у вигляді розривів на краях між клітинами. Подивіться на наступні приклади:
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Пізніше у 2012 році Inigo Quilez написав статтю про створення точних меж для комірок Вороного.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
На цьому експерименти Inigo з Вороним не закінчилися. У 2014 році він написав чудову статтю про функцію voro-noise, яка дозволяє поступово змішувати звичайний шум з комірками Вороного. Ось вирізка з його слів:
"Попри схожість, сітка в обох патернах використовується по різному. Шум інтерполює/усереднює випадкові значення (як у шумі значення) або градієнти (як у градієнтному шумі), тоді як алгоритм Вороного обчислює відстань до найближчої опорної точки. Плавна білінійна інтерполяція та обчислення мінімума — дві дуже різні операції чи... ні? Чи можна їх об’єднати у більш загальну метрику? Якщо так, то шум та патерни Вороного можна розглядати як окремі випадки більш загального генератора патернів на основі регулярної сітки?»
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Настав час придивитися до навколишніх речей, надихнутися природою, знайти власну ідею та спробувати сили у цій техніці!
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
---
12/README Vi (12/README-vi.md)
Nhiễu mô phỏng tế bào (Cellular noise)
Vào năm 1996, mười sáu năm sau khi thuật toán sinh nhiễu đầu tiên của Perlin được công bố, và năm năm sau khi Thế giới biết đến thuật toán Simplex Noise của ông, Steven Worley đã viết một báo cáo khoa học với tên gọi "Vân bề mặt mô phỏng tế bào" (A Cellular Texture Basis Function). Trong nghiên cứu của mình, ông ấy đã mô tả một kỹ thuật sinh vân bề mặt mà giờ đây được sử dụng rộng khắp bởi cộng đồng đồ hoạ.
Để hiểu được các nguyên lý đằng sau, ta cần tư duy theo kiểu duyệt (iteration). Có thể bạn nghe thấy quen quen, đúng, ta đã suy nghĩ tương tự khi sử dụng vòng lặp
`for` rồi. Có một hạn chế về vòng lặp `for` trong GLSL, đó là: số vòng lặp phải cố định. Hãy cùng nhìn một ví dụ.
Các điểm trong một distance field
Nhiễu mô phỏng tế bào (Cellular noise) dựa trên distance field, khoảng cách tới một trong số những đỉnh gần nhất. Giả sử ta muốn tạo một distance field có 4 đỉnh. Ta phải làm gì ? Với mỗi điểm ảnh, tính khoảng cách tới đỉnh gần nhất. Điều đó có nghĩa là ta cần phải duyệt qua tất cả các đỉnh, tính khoảng cách từ điểm ảnh tới từng đỉnh một và lưu lại khoảng cách gần nhất.
glslfloat min_dist = 100.; // Biến lưu lại khoảng cách gần nhất min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
Nhìn thì không tinh tế lắm nhưng được việc. Giờ hãy chuyển đoạn code kia thành một vòng lặp nhé.
glslfloat m_dist = 100.; // khoảng cách nhỏ nhất
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
Chú ý cách tôi dùng vòng lặp
`for` để duyệt qua mảng chứa vị trí từng đỉnh, và lưu lại khoảng cách nhỏ nhất bằng hàm `min()`. Đoạn code dưới đây làm đúng như vậy:<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
Trong đoạn code phía trên, một trong các đỉnh là vị trí của con trỏ chuột. Di chuyển con trỏ chuột để có minh hoạ trực quan về thuật toán này nhé. Sau đó hãy thử nghĩ:
- Làm sao để di chuyển các đỉnh còn lại ?
- Sau khi đọc Chương hình dạng, hãy nghĩ ra các cách khác để sử dụng distance field
- Nếu muốn thêm nhiều đỉnh hơn thì làm thế nào ? Nếu muốn tự do thêm bớt đỉnh thì sao ?
Vòng lặp duyệt các phần tử
Bạn có thể đã nhận ra vòng lặp
`for` và mảng không phải là bạn tốt của GLSL. Như tôi đã nói trước đó, vòng lặp yêu cầu cố định số vòng lặp ngay từ đầu. Ngoài ra thì việc duyệt từng phần tử cũng khiến code shader chậm hơn đáng kể. Điều đó có nghĩa là nếu có nhiều đỉnh ta không thể dùng cách này. Ta phải tìm một chiến thuật khác, sao cho tận dụng được ưu thế về kiến trúc tính toán song song của GPU.Một giải pháp được đưa ra là chia nhỏ không gian thành nhiều phần. Đâu nhất thiết phải tính khoảng cách của từng điểm ảnh với tất cả các đỉnh, phải không ? Giả sử mỗi điểm ảnh được xử lý trên một thread riêng, ta có thể chia canvas thành nhiều ô, mỗi ô chỉ có 1 đỉnh trong đó, gọi là nhân. Để tránh lỗi ở phần biên giới giữa các ô, ta sẽ cần tính khoảng cách của các pixel trong mỗi ô với nhân của các ô lân cận nữa. Đó chính là ý tưởng chủ đạo trong nghiên cứu của Steven Worley. Tóm lại, mỗi điểm ảnh chỉ cần tính khoảng cách với tối đa 9 nhân: 1 nhân ở cùng ô với điểm ảnh, và 8 nhân ở các ô xung quanh. Ta cũng đã biết cách chia nhỏ canvas thành nhiều ô ở các chương trước khi bàn về: Mẫu họa tiết, Sự ngẫu nhiên và Nhiễu, nên tôi hy vọng bạn đã thuần thục kỹ năng này rồi.
glsl// Phóng to
st *= 3.; // Chia nhỏ
vec2 i_st = floor(st);
vec2 f_st = fract(st);
Vậy, kế hoạch là gì ? Ta sẽ dùng toạ độ hàng cột (phần nguyên) của mỗi ô (lưu trong
`i_st`) để sinh một cặp số ngẫu nhiên, chính là vị trí của nhân trong ô đó. Hàm `random2`
nhận một `vec2` và trả về một `vec2` gồm 2 số ngẫu nhiên, chính là để thực hiện việc sinh ra một nhân có vị trí ngẫu nhiên trong ô này.glslvec2 point = random2(i_st);
Mỗi điểm ảnh trong ô này sẽ tính khoảng cách từ toạ độ của chính nó (lưu trong
`f_st`) với nhân.glslvec2 diff = point - f_st;
float dist = length(diff);
Kết quả thu được như sau:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Ta sẽ cần tính thêm khoảng cách tới nhân của các ô xung quanh nữa. Để làm được việc đó, ta cần duyệt qua các ô xung quanh, tức là các ô nằm trong phạm vi 3x3 có toạ độ X và Y trong khoảng [-1, 1] so với ô hiện tại. Ta có thể dùng 2 vòng lặp
`for` lồng nhau để làm việc đó:glslfor (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Các ô liền kề trong lưới
vec2 neighbor = vec2(float(x),float(y));
...
}
}
Hãy nhớ rằng các hàm ngẫu nhiên không thật sự ngẫu nhiên, vì thế ta có thể biết vị trí của nhân ở một ô bất kỳ bằng cách gọi lại hàm random2 với tham số giống như ở chính ô đó vậy.
glsl...
// Tính vị trí nhân ở ô liền kề
vec2 point = random2(i_st + neighbor);
...
Phần còn lại chỉ là tính khoảng cách với các nhân và lưu lại khoảng cách nhỏ nhất vào biến
`m_dist`.glsl...
vec2 diff = neighbor + point - f_st; // Khoảng cách tới nhân
float dist = length(diff);
// Lưu khoảng cách nhỏ nhất
m_dist = min(m_dist, dist);
...
Đoạn code trên lấy cảm hứng từ một bài báo của Inigo's Quilez mà trong đó ông có viết:
"... đoạn code trên có thể cải tiến hơn nữa chỉ nhờ một mẹo nhỏ. Các đoạn code tương tự có rủi ro về sai số, vì vị trí nhân được lưu là toạ độ tuyệt đối. Ta có thể sử dụng các kiểu dữ liệu có độ chính xác cao hơn để khắc phục, nhưng còn cách khác hay hơn. Code của tôi không dùng toạ độ tuyệt đối mà chỉ dùng toạ độ tương đối trong mỗi ô thôi: sau khi tách được phần nguyên và phần thập phân, việc sinh vị trí ngẫu nhiên của nhân được thực hiện giống nhau trong mọi ô, vì thế mà phần nguyên không còn cần thiết nữa, tiết kiệm rất nhiều không gian bộ nhớ. Thực tế thì ở các đoạn code sinh voronoi thông thường, tuy sử dụng toạ độ tuyệt đối nhưng nó vẫn phải đảm bảo nằm trong ô, tức là vẫn có ràng buộc gián tiếp về toạ độ tương đối. Còn ở đoạn code trên, ta thậm chí không cần quan tâm tới ràng buộc đó, bởi toạ độ được sinh ra chắc chắn sẽ nằm trong ô. Mẹo nhỏ này có thể áp dụng cả trong trường hợp số lượng điểm ảnh khổng lồ - ta sẽ cần sử dụng kiểu dữ liệu double có khoảng giá trị lớn hơn float, nhưng ngay sau khi tách được phần thập phân, ta chỉ cần làm việc với phần thập phân lưu bằng một biến float là đủ. Tất nhiên là mẹo này cũng có thể áp dụng cho Perlin noise (nhưng tôi chưa từng thấy nó được implement hay mô tả theo cách này)."
Tóm tắt: Ta chia canvas thành nhiều ô, mỗi ô có một nhân; mỗi điểm ảnh sẽ tính khoảng cách tới nhân trong ô tương ứng và cả 8 ô xung quanh; lưu lại khoảng cách nhỏ nhất. Kết quả thu được là 1 distance field trông như sau:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Hãy thử vài thứ phức tạp hơn:
- Chia canvas thành lưới có kích thước khác
- Làm thế nào để di chuyển các nhân trong mỗi ô ?
- Nếu ta coi vị trí của con trỏ chuột là một nhân thì sao ?
- Nếu không dùng công thức
`m_dist = min(m_dist, dist);` thì còn cách nào khác để tìm khoảng cách gần nhất không ?
- Dùng distance field này tạo được các họa tiết nào ?Thuật toán này cũng có thể được mô tả theo cách khác, thay vì bắt đầu bằng các điểm ảnh ta sẽ lấy các nhân làm cơ sở. Cụ thể như sau: Mỗi nhân sẽ tự lan rộng vùng ảnh hưởng ra xung quanh tới khi chạm với các vùng ảnh hưởng khác. Trong tự nhiên thì nhiều loài động thực vật trưởng thành theo cách này. Các hình thái sinh vật sống được hình thành bởi hai tác động chính: một là nội lực đẩy từ bên trong ra khiến nó ngày càng sinh sôi và lớn hơn, hai là ngoại lực từ bên ngoài kiềm chế mức độ sinh trưởng lẫn nhau. Thuật toán mô phỏng theo quan sát này được đặt tên theo nhà toán học đã định nghĩa nó, đó là Georgy Voronoi.
Thuật toán Voronoi
Việc tạo ra sơ đồ Voronoi từ cellular noise không khó như hình dung. Ta chỉ cần tốn thêm ít bộ nhớ để lưu các thông tin. Để làm điều đó ta sẽ dùng một biến kiểu
`vec2` có tên là `m_point`. Bằng cách lưu lại hướng từ điểm ảnh tới nhân gần nhất, thay vì chỉ có thông tin về khoảng cách, ta còn xác định cả đỉnh gần nhất luôn.glsl...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
Trong đoạn code dưới đây, tôi không dùng hàm
`min` nữa mà dùng lệnh `if` cơ bản. Vì tôi không chỉ muốn lưu lại khoảng cách gần nhất mà còn muốn biết nhân gần nhất nữa (dòng 32 tới 37).<div class="codeAndCanvas" data="vorono-00.frag"></div>
Nếu bạn di chuyển con trỏ chuột trong canvas, bạn sẽ thấy con trỏ chuột cũng được tính là một nhân. Tôi làm vậy để bạn có thể hiểu hơn về thuật toán này. Thậm chí, màu của các pixel sẽ thay đổi theo toạ độ con trỏ chuột.
Giờ hãy chuyển qua thuật toán tương tự được mô tả trong báo cáo của Steven Worley. Bạn hãy tự code thuật toán này. Bạn cũng có thể tham khảo đoạn code dưới đây bằng cách click vào ảnh minh hoạ. Thuật toán ban đầu của Steven Worley có nhiều nhân trong một ô thay vì chỉ có một nhân, nhưng vì ông dùng ngôn ngữ C để lập trình, nên có thể thay đối số vòng lặp tuỳ ý. GLSL không cho phép điều đó, nên ta sẽ giới hạn số nhân ở mỗi ô là 1.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Khi bạn đã hiểu thuật toán này rồi, hãy nghĩ ra cách để tận dụng nó.
Cải tiến Voronoi
Năm 2011, Stefan Gustavson đã cải tiến thuật toán của Steven Worley cho GPU chỉ bằng cách duyệt qua ma trận 2x2 thay vì 3x3. Điều này giúp giảm số lượng phép tính đáng kể, nhưng cũng dễ làm cho các cạnh không đồng đều. Hãy thử nhìn các ví dụ sau:
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Khoảng cuối năm 2012, Inigo Quilez đã đăng một bài báo về cách vẽ các đường viền Voronoi một cách chính xác.
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Thí nghiệm của Inigo với Voronoi không chỉ dừng lại ở đó. Vào năm 2014, ông đã công bố một nghiên cứu thú vị mà ông gọi là voro-noise, một hàm có khả năng chuyển tiếp giữa nhiễu ngẫu nhiên và Voronoi. Trong báo cáo đó ông có viết:
"Dù cả hai thuật toán sinh nhiễu đều dùng hệ thống lưới nhưng cách dùng rất khác nhau. Value noise thì nội suy giữa các đỉnh có giá trị đi kèm ngẫu nhiên, Gradient noise thì nội suy giữa các dải màu ngẫu nhiên, còn Voronoi thì tính khoảng cách tới các đỉnh ngẫu nhiên. Nếu cả nội suy có lọc bilinear và công thức xác định khoảng cách gần nhất có thể tổng quát hoá thành một phép toán thì cả nhiễu thông thường và sơ đồ Voronoi sẽ được quy về 2 trường hợp của cùng một thuật toán sinh họa tiết trên một lưới xác định phải không ?"
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Bạn hãy quan sát thật kỹ thế giới xung quanh, thiên nhiên đầy nhiệm màu và sẽ giúp bạn tự tìm ra kỹ thuật sinh nhiễu của riêng mình !
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>
---
13/README (13/README.md)
Fractal Brownian Motion
Noise tends to mean different things to different people. Musicians will think of it in terms of disturbing sounds, communicators as interference and astrophysicists as cosmic microwave background radiation. These concepts bring us back to the physical reasons behind randomness in the world around us. However, let's start with something more fundamental, and more simple: waves and their properties. A wave is a fluctuation over time of some property. Audio waves are fluctuations in air pressure, electromagnetical waves are fluctuations in electrical and magnetic fields. Two important characteristics of a wave are its amplitude and frequency. The equation for a simple linear (one-dimensional) wave looks like this:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* Try changing the values of the frequency and amplitude to understand how they behave.
* Using shaping functions, try changing the amplitude over time.
* Using shaping functions, try changing the frequency over time.
By doing the last two exercises you have managed to "modulate" a sine wave, and you just created AM (amplitude modulated) and FM (frequency modulated) waves. Congratulations!
Another interesting property of waves is their ability to add up, which is formally called superposition. Comment/uncomment and tweak the following lines. Pay attention to how the overall appearance changes as we add waves of different amplitudes and frequencies together.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01(-u_time130.0);
y += sin(xfrequency2.1 + t)*4.5;
y += sin(xfrequency1.72 + t1.121)4.0;
y += sin(xfrequency2.221 + t0.437)5.0;
y += sin(xfrequency3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Experiment by changing the frequency and amplitude for the additional waves.
* Is it possible to make two waves cancel each other out? What will that look like?
* Is it possible to add waves in such a way that they will amplify each other?
In music, each note is associated with a specific frequency. The frequencies for these notes follow a pattern which we call a scale, where a doubling or halving of the frequency corresponds to a jump of one octave.
Now, let's use Perlin noise instead of a sine wave! Perlin noise in its basic form has the same general look and feel as a sine wave. Its amplitude and frequency vary somewhat, but the amplitude remains reasonably consistent, and the frequency is restricted to a fairly narrow range around a center frequency. It's not as regular as a sine wave, though, and it's easier to create an appearance of randomness by summing up several scaled versions of noise. It is possible to make a sum of sine waves appear random as well, but it takes many different waves to hide their periodic, regular nature.
By adding different iterations of noise (octaves), where we successively increment the frequencies in regular steps (lacunarity) and decrease the amplitude (gain) of the noise we can obtain a finer granularity in the noise and get more fine detail. This technique is called "fractal Brownian Motion" (fBM), or simply "fractal noise", and in its simplest form it can be created by the following code:
<div class="simpleFunction" data="// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Initial values
float amplitude = 0.5;
float frequency = 1.;
//
// Loop of octaves
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* Progressively change the number of octaves to iterate from 1 to 2, 4, 8 and 10. See what happens.
* When you have more than 4 octaves, try changing the lacunarity value.
* Also with >4 octaves, change the gain value and see what happens.
Note how with each additional octave, the curve seems to get more detail. Also note the self-similarity while more octaves are added. If you zoom in on the curve, a smaller part looks about the same as the whole thing, and each section looks more or less the same as any other section. This is an important property of mathematical fractals, and we are simulating that property in our loop. We are not creating a true fractal, because we stop the summation after a few iterations, but theoretically speaking, we would get a true mathematical fractal if we allowed the loop to continue forever and add an infinite number of noise components. In computer graphics, we always have a limit to the smallest details we can resolve, for example when objects become smaller than a pixel, so there is no need to make infinite sums to create the appearance of a fractal. A lot of terms may be needed sometimes, but never an infinite number.
The following code is an example of how fBm could be implemented in two dimensions to create a fractal-looking pattern:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* Reduce the number of octaves by changing the value on line 37
* Modify the lacunarity of the fBm on line 47
* Explore by changing the gain on line 48
This technique is commonly used to construct procedural landscapes. The self-similarity of the fBm is perfect for mountains, because the erosion processes that create mountains work in a manner that yields this kind of self-similarity across a large range of scales. If you are interested in this use, you should definitely read this great article by Inigo Quiles about advanced noise.
Using more or less the same technique, it's also possible to obtain other effects like what is known as turbulence. It's essentially an fBm, but constructed from the absolute value of a signed noise to create sharp valleys in the function.
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Another member of this family of algorithms is the ridge, where the sharp valleys are turned upside down to create sharp ridges instead:
glsln = abs(n); // create creases
n = offset - n; // invert so creases are at top
n = n * n; // sharpen creases
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Another variant which can create useful variations is to multiply the noise components together instead of adding them. It's also interesting to scale subsequent noise functions with something that depends on the previous terms in the loop. When we do things like that, we are moving away from the strict definition of a fractal and into the relatively unknown field of "multifractals". Multifractals are not as strictly defined mathematically, but that doesn't make them less useful for graphics. In fact, multifractal simulations are very common in modern commercial software for terrain generation. For further reading, you could read chapter 16 of the book "Texturing and Modeling: a Procedural Approach" (3rd edition), by Kenton Musgrave. Sadly, that book is out of print since a few years back, but you can still find it in libraries and on the second hand market. (There's a PDF version of the 1st edition available for purchase online, but don't buy that - it's a waste of money. It's from 1994, and it doesn't contain any of the terrain modeling stuff from the 3rd edition.)
Domain Warping
Inigo Quiles wrote this other fascinating article about how it's possible to use fBm to warp a space of a fBm. Mind blowing, Right? It's like the dream inside the dream of Inception.
A less extreme example of this technique is the following code where the wrap is used to produce this clouds-like texture. Note how the self-similarity property is still present in the result.
<div class='codeAndCanvas' data='clouds.frag'></div>
Warping the texture coordinates with noise in this manner can be very useful, a lot of fun, and fiendishly difficult to master. It's a powerful tool, but it takes quite a bit of experience to use it well. A useful tool for this is to displace the coordinates with the derivative (gradient) of the noise. A famous article by Ken Perlin and Fabrice Neyret called "flow noise" is based on this idea. Some modern implementations of Perlin noise include a variant that computes both the function and its analytical gradient. If the "true" gradient is not available for a procedural function, you can always compute finite differences to approximate it, although this is less accurate and involves more work.
#### For your toolbox
* LYGIA's generative functions are a set of reusable functions to generate patterns in GLSL. It's a great resource to learn how to use randomness and noise to create generative art. It's very granular library, designed for reusability, performance and flexibility. And it can be easily be added to any projects and frameworks.
---
13/Notes (13/notes.md)
https://docs.google.com/spreadsheets/d/194IVZR_xLVsw5H0zJZ7CWuimOQA_n8KU2eeIZBDwIH0/edit#gid=0
http://heman.readthedocs.org/en/latest/generate.html#archipelagos
http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Tatarchuk-Noise(GDC07-D3D_Day).pdf
http://www.campi3d.com/External/MariExtensionPack/help/MARI%20Extension%20Pack.html?Understandingsomebasicnoiseterms.html
http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/white-paper-procedural-terrain-generation-with-r2452
http://www.iquilezles.org/www/articles/warp/warp.htm
http://www.iquilezles.org/www/articles/morenoise/morenoise.htm
https://github.com/NVIDIAGameWorks/OpenGLSamples/blob/master/samples/es3aep-kepler/TerrainTessellation/assets/shaders/noise.glsl
---
13/README De (13/README-de.md)
Gebrochene Brownsche Bewegung
Rauschen kann für Menschen ganz unterschiedliche Dinge bedeuten, je nachdem, wen man danach fragt. Musiker denken bei diesem Stichwort an schlechte Klangqualität, Funkspezialisten an unerwünschte Überlagerungen und Astrophysiker an die kosmische Hintergrundstrahlung im Mikrowellenbereich. Diese Vorstellungen führen uns zurück zu den physikalischen Grundlagen des Zufälligen in unserer Welt.
Doch lass uns erst einmal mit etwas ganz Einfachem und Fundamentalem beginnen: Mit den Eigenschaften von Wellen. Eine Welle steht für die Schwankung einer bestimmten Eigenschaft im Verlauf der Zeit. Audiowellen sind Schwankungen des Luftdrucks, elektromagnetische Wellen sind Schwankungen elektrischer und magnetischer Felder. Zwei spezifische Merkmale von Wellen sind ihre Amplitude (die Intensität ihres Ausschlags) und ihre Frequenz (die Häufigkeit, mit der sich die Welle in einer bestimmten Zeit wiederholt). Die Formel für eine einfache lineare Welle (in einer Dimension) sieht so aus:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequenz = 1.;
y = amplitude sin(x frequenz);
"></div>
* Verändere in der obigen Formel doch einfach mal die Werte für die Variablen
`amplitude` und `frequenz` und beobachte, was dann geschieht.
* Versuche eine formgebende Funktion in die Formel einzubringen, so dass sich die Amplitude im Verlauf der Zeit ändert.
* Versuche das Gleiche, aber diesmal für eine Änderung der Frequenz im Laufe der Zeit.Mit den letzten beiden Übungen modulierst Du die Sinuswelle. So erhältst Du einmal eine amplitudenmodulierte Welle (AM) und einmal eine frequenzmodulierte Welle (FM).
Ein weiteres interessantes Merkmal von Wellen kann man beobachten, wenn man mehrere Wellen addiert. Dabei entsteht ein Phänomen, das als „Überlagerung“ bekannt ist. Kommentiere in dem folgenden Beispiel einzelne Zeilen aus bzw. wieder ein und verändere die verschiedenen Faktoren für die Frequenz und die Amplitude. Verschaffe Dir einen Eindruck davon, wie sich das Gesamtbild auf diese Weise ändert.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequenz = 1.;
y = sin(x * frequenz);
float t = 0.01(-u_time130.0);
y += sin(xfrequenz2.1 + t)*4.5;
y += sin(xfrequenz1.72 + t1.121)4.0;
y += sin(xfrequenz2.221 + t0.437)5.0;
y += sin(xfrequenz3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Experimentiere mit Veränderungen der Amplitude und der Frequenz bei den hinzuaddierten Wellen.
* Gelingt es Dir, zwei Wellen zu erschaffen, die sich gegenseitig aufheben? Wie würde das aussehen?
* Ist es möglich, Wellen auf eine bestimmte Art und Weise zu addieren, so dass sie sich gegenseitig verstärken?
In der Musik steht jede Note für eine bestimmte Frequenz. Reiht man die Frequenzen der Noten einer Tonleiter aneinander, so ergibt sich ein charakteristisches Muster. Die Halbierung bzw. Verdoppelung der Frequenz entspricht dabei jeweils dem Sprung um eine Oktave nach unten bzw. oben.
Okay, lass uns die Sinuswelle nun durch Perlins Rauschfunktion ersetzen. Diese hat in ihrer einfachsten Form ein ähnliches Erscheinungsbild wie die Sinuswelle. Zwar sind die Frequenz und die Amplitude von Perlins Rauschfunktion nicht konstant, aber die Amplitude bleibt zumindest weitgehend gleich und die Frequenz schwankt nur in einem schmalen Bereich rund um die Basisfrequenz. Perlins Rauschfunktion ist dadurch nicht so gleichförmig wie eine Sinuswelle. So wird es einfacher, den Eindruck von Zufälligkeit zu erzielen, indem man mehrere dieser Funktionen erst skaliert und dann addiert. Zwar ist dies grundsätzlich auch mit Sinuswellen möglich. Doch benötigt man dann eine größere Anzahl einzelner Wellen, um den eigentlich so regelmäßigen und periodischen Charakter der zugrundeliegenden Sinuswelle zu verbergen.
Indem wir das Ergebnis mehrerer Rauschfunktionen (oktaven) addieren, wobei wir schrittweise die Frequenz durch Multiplikation um einen gleichbleibenden Faktor (porositaet) steigern und die Amplitude dieser Zuwächse verringern (zuwachs < 1.0), verleihen wir unserem Rauschen eine feinere Granularität mit mehr Details. Diese Technik ist als „Gebrochene Brownsche Bewegung“ (engl.: „fractal Brownian motion“), kurz fBm, oder auch einfach als „frakturiertes Rauschen“ (engl.: „fractal noise“) bekannt. In ihrer simpelsten Form kann man sie mit Hilfe des folgenden Codes erzeugen:
<div class="simpleFunction" data="// Properties
const int oktaven = 1;
float porositaet = 2.0;
float zuwachs = 0.5;
//
// Startwerte
float amplitude = 0.5;
float frequenz = 1.;
//
// die Oktaven durchlaufen
for (int i = 0; i < oktaven; i++) {
	y += amplitude noise(frequenzx);
	frequenz *= porositaet;
	amplitude *= zuwachs;
}"></div>
* Erhöhe die Anzahl der Oktaven schrittweise von 1 auf 2, 4, 8 und 10 und beobachte, was dann geschieht.
* Sobald Du mehr als 4 Oktaven durchläufst, ändere den Wert für die Porosität.
Ändere dann (bei oktaven > 4*) auch den Wert für den Zuwachs, um die Auswirkungen zu beobachten.
Fällt Dir auf, dass der Funktionsgraph mit jeder zusätzlichen Oktave an Details gewinnt? Außerdem wächst die Selbstähnlichkeit: Der Kurvenverlauf in den einzelnen kleinen Abschnitten ähnelt immer mehr dem Verlauf im Großen. Dies ist ein wichtiges Merkmal mathematischer „Fraktale“ und wir simulieren dies hier durch die Oktaven-Schleife. Zwar erzeugen wir kein echtes Fraktal, weil wir die Aufsummierung nach einigen Schleifendurchläufen stoppen. Wenn wir die Schleife aber unendlich fortsetzen und immer weitere Rausch-Komponenten hinzufügen würden, erhielten wir tatsächlich ein vollständiges Fraktal.
Im Bereich der Computergrafik existieren immer Beschränkungen in der Hinsicht, wie fein wir einzelne Details auflösen können. Werden die Objekte beispielsweise kleiner als ein Pixel, macht es gar keinen Sinn mehr, weiterzurechnen. Manchmal bedarf es einer großen Anzahl an Schleifendurchläufen für hochauflösende Details, aber eine endlose Anzahl an Schleifendurchläufen und Details sind grundsätzlich nicht sinnvoll.
Der folgende Programmcode liefert ein Beispiel dafür, wie man eine fBm in zwei Dimensionen implementieren kann, um ein Muster ähnlich einem Fraktal zu erzeugen:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
Reduziere die Anzahl der Oktaven, indem Du den Wert der Konstanten in Zeile 37* änderst.
Ändere die Porosität der fBm in Zeile 47*.
Untersuche die Auswirkungen, wenn Du die Verstärkung in Zeile 48* veränderst.
Diese Technik wird in der Computergrafik häufig angewandt, um auf prozedurale Weise künstliche Landschaften zu erzeugen. Die Selbstähnlichkeit als Merkmal einer fBm ist perfekt für die Erzeugung von Bergen, Bergketten und ihren Tälern geeignet. Schließlich erzeugt die Erosion, die diese Strukturen in der Natur hervorbringt, ebenfalls eine Selbstähnlichkeit in mehreren unterschiedlichen Größenordnungen – im Großen wie im Kleinen. Falls Dich das Thema interessiert, empfehle ich Dir diesen hervorragenden Beitrag von Inigo Quiles über hochentwickeltes Rauschen.
Mit mehr oder weniger derselben Technik lassen sich auch andere Effekte nachahmen, beispielsweise Turbulenzen. Dies geschieht mit einer fBm, wobei allerdings der Absolutwert einer vorzeichenbehafteten Rauschfunktion genutzt wird, um starke Täler zu erzeugen.
glslfor (int i = 0; i < OKTAVEN; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Ein weiteres Mitglied aus dieser Familie von Funktionen ist der Bergrücken, bei dem die tiefen Täler nach oben gekehrt werden, um scharfe Bergrücken bzw. Bergkämme zu erzeugen:
glsln = abs(n); // erzeuge eine Falte
n = offset - n; // hole den Faltenboden nach oben
n = n * n; // intensiviere die Ausbuchtung
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Eine andere Variante dieses Verfahrens mit interessanten Ergebnissen besteht in der Multiplikation der einzelnen Rauschelemente anstelle der Addition. Man kann außerdem die Skalierung nicht gleichförmig mit jeder Detailebene (jedem Schleifendurchlauf) fortsetzen, sondern von den Ergebnissen vorheriger Schleifendurchläufe abhängig machen. Damit verlässt man allerdings die Welt klassischer Fraktale und dringt in das noch wenig erforschte Feld der „Multifraktale“ vor. Diese sind mathematisch nicht streng definiert, aber das macht sie für die Computergrafik nicht weniger nützlich. Tatsächlich werden Multifraktale bereits vielfältig in Software für die künstliche Erzeugung von Landschaftsstrukturen eingesetzt.
Falls Dich dieses Thema interessiert, kannst Du darüber z.B. in Kapitel 16 des Buches „Texturing and Modeling: a Procedural Approach“ (dritte Auflage) von Kenton Musgrave, mehr erfahren. Leider ist das Buch seit einigen Jahren vergriffen, aber man findet es noch in Bibliotheken und auf dem Gebrauchtmarkt. (Eine PDF-Version der ersten Auflage wird im Internet verkauft, aber die stammt von 1994 und enthält leider noch nicht die hier empfohlenen Kapitel.)
Raumkrümmung
Inigo Quiles hat einen weiteren faszinierenden Artikel darüber verfasst, wie man ein fBm einsetzen kann, um einen Raum aus fBm zu verzerren. Abgefahren, nicht wahr? Das ist wie ein Traum über einen Traum, in dem es richtig rundgeht.
Ein etwas weniger extremes Beispiel für diese Technik liefert der folgende Programmcode, wo mit Hilfe der Verzerrung wolkenartige Strukturen erzeugt werden. Auch hier spielt das Merkmal der Selbstähnlichkeit wiederum eine wichtige Rolle.
<div class='codeAndCanvas' data='clouds.frag'></div>
Die Texturkoordinaten mit Hilfe einer Rauschfunktion auf diese Weise zu verzerren, kann sehr nützlich sein und tolle Resultate liefern, es ist aber auch nicht ganz einfach zu beherrschen. Als hilfreiches Werkzeug erweist sich dabei der Ersatz der Koordinaten durch eine Ableitung (den Gradienten) der Rauschfunktion. Ein berühmter Artikel von Ken Perlin und Fabrice Neyret mit dem Titel „flow noise“ erläutert diesen Ansatz.
Einige moderne Implementationen von Perlins Noise-Algorithmus berechnen sowohl den Funktionswert als auch den Verlauf. Wenn der konkrete Verlauf im Rahmen der prozeduralen Berechnung nicht verfügbar ist, kann man immer noch eine begrenzte Anzahl von Differenzen berechnen, um sich dem Verlauf anzunähern, auch wenn dies mehr Arbeit bedeutet und keine ganz exakten Ergebnisse liefert.
---
13/README Fr (13/README-fr.md)
Fractional Brownian Motion
Le bruit peut avoir plusieurs significations selon les personnes. Les musiciens le trouveront dérangeant, les communicants le considèrent comme une interférence et les astrophysiciens comme un rayonnement cosmique.
Toutes ces qualifications, nous ramènent à l'ancrage physique du bruit dans notre environnement. Commençons toutefois par quelque chose de plus simple et de plus fondamental ; les ondes et leurs propriétés.
Une onde peut être considérée comme la variation d'une propriété dans le temps ; le son est une variation de la pression de l'air au fil du temps, une onde électro-magnétique est la fluctuation dans le temps d'un champs électrique et magnétique, etc.
Les deux caractéristiques importantes d'une onde sont sa fréquence et son amplitude.
L'équation d'une onde à une dimension peut s'écrire comme suit :
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* Essayez de changer les valeurs de fréquence et d'amplitude pour comprendre leurs effets.
* À l'aide des fonctions de formes, faites varier l'amplitude au fil du temps.
* Faites de même avec la fréquence.
Félicitations ! En suivant les deux dernières instructions, vous avez réussi à "moduler" l'onde et à créer une modulation de fréquence (FM) et d'amplitude (AM), et oui, exactement comme des ondes radio !
Une seconde propriété intéressante des ondes est leur capacité à s'additionner, ce qu'on appelle la superposition.
Commentez/décommentez et jouez avec les les lignes suivantes en vous intéressant à la forme que prend l'onde lorsqu'on la combine à d'autres.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01(-u_time130.0);
y += sin(xfrequency2.1 + t)*4.5;
y += sin(xfrequency1.72 + t1.121)4.0;
y += sin(xfrequency2.221 + t0.437)5.0;
y += sin(xfrequency3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Changez les valeurs de fréquence et d'amplitude des ondes additionnelles.
* Pouvez vous créer deux ondes qui s'annulent ? A quoi ressemblera l'onde finale ?
* Est-il possible d'additionner des ondes de manière à ce qu'elles s'amplifient l'une l'autre ?
En musique, chaque note est associée à une fréquence particulière. Les fréquences correspondent aux _notes_ de musique et doubler ou diviser par deux une fréquence permet de changer d'_octave_.
Utilisons à présent un bruit de Perlin au lieu d'une sinusoïde !
Un bruit de Perlin de base ressemble globalement à une sinusoïde.
Son amplitude et sa fréquence varient un peu mais l'amplitude reste globalement la même tandis que la fréquence
reste cantonnée dans une zone restreinte autour de la fréquence centrale.
Cependant, ce n'est pas une sinusoïde régulière et il est plus simple d'atteindre un résultat pseudo-aléatoire
en ajoutant plusieurs versions du bruit à différentes échelles (amplitudes).
Il est possible d'obtenir le même résultat avec des sinusoïdes mais il est nécessaire de combiner un nombre d'ondes important pour masquer leur nature périodique.
En ajoutant différentes itérations du bruit (différents octaves),
dont on augmente la fréquence (la lacunarité) et dont on réduit l'amplitude (le gain), on obtient une granularité
qui nous permet de préserver les détails fins d'un bruit.
Cette technique s'appelle "Fractional Brownian Motion" (FBM) ou simplement bruit fractal.
Voici un exemple d'implémentation :
<div class="simpleFunction" data="// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Initial values
float amplitude = 0.5;
float frequency = 1.;
//
// Loop of octaves
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* Changez progressivement le nombre d'octaves de 1 à 10 et regardez ce qui se produit.
* Au delà de 4 octaves, changez la valeur de lacunarité.
* Toujours au delà de 4 octaves, changez le gain et observez le résultat.
Notez comment, à chaque nouvel octave, la courbe semble gagner en détail.
Notez également lors de l'ajout d'octaves que lorsqu'on zoome sur la courbe, les plus petits éléments ressemblent à l'ensemble et inversement ; c'est ce qu'on appelle l'auto-similarité !
C'est une propriété importante des fractales et nous la simulons dans notre boucle.
Nous ne créons pas une fractale à proprement parler puisque nous arrêtons l'ajout de bruit après quelques itérations mais d'un point de vue théorique,
si nous pouvions laisser la boucle tourner indéfiniment et ajouter une somme infinies de bruits, nous obtiendrions une courbe fractale.
Dans un shader, la finesse du détail est limitée par la résolution écran ; si le résultat devient plus petit qu'un pixel, il n'ya pas vraiment de raison (ni de moyen) de le représenter à l'écran.
Nous avons donc pas besoin de boucles infinies pour obtenir une apparence fractale, il faut parfois un grand nombre d'itérations mais jamais une infinité.
Le code suivant est un exemple d'implémentation de FBM en 2 dimensions :
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* Réduisez le nombre d'octaves en changeant la ligne 37
* Changez la lacunarité du FBM à la ligne 47
* Changez le gain ligne 48
Cette technique est communément utilisée pour créer des terrains procéduraux.
L'auto-similarité du FBM se prête bien au rendu de montagnes : le processus d'érosion qui donne leur forme aux montagnes
produit le même genre de motifs auto-similaires à grande échelle.
Si le sujet vous intéresse, nous vous invitons à lire cet article d'Inigo Quiles sur les techniques de bruit avancées.
Le principe du FBM peut être amendé pour obtenir différents effets comme par exemple cette turbulence.
On part de la structure de notre FBM mais au lieu d'accumuler la valeur signée (+/-) du bruit, on accumule sa valeur absolue (+) ce qui crée des vallées et des collines.
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Une seconde variante dite ridge noise (bruit de crête ou d'arête) consiste à inverser les vallées :
glsln = abs(n); // create creases
n = offset - n; // invert so creases are at top
n = n * n; // sharpen creases
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Une autre variante consiste à multiplier les valeurs de bruit au lieu de les additionner.
Il est intéressant de modifier l'échelle d'une itération de bruit en fonction du bruit de l'itération precédente.
En faisant ce genre de chose, nous nous éloignons du monde des fractales et entrons dans le monde méconnu des multifractales.
Les multifractales ne sont pas aussi clairement définies mathématiquement que les fractales ce qui ne nous empêche pas de nous en servir dans les shaders.
Les simulations multifractales sont d'ailleurs très répandues dans les logiciels de génération de terrain.
Vous trouverez plus d'informations sur ce sujet au chapitre 16 de "Texturing and Modeling: a Procedural Approach" (3ème édition), de Kenton Musgrave.
Malheureusement le livre n'est plus édité depuis quelques années déjà mais vous le trouverez en bibliothèque ou d'occasion.
Il est possible d'acheter un PDF de la première édition en ligne mais ça ne vaut pas le coup ; elle date de 1994 et ne contient aucune information sur la génération de terrain.
Domain Warping
Inigo Quiles a également écrit cet article fascinant sur le fait de "plier" ou "recouvrir" (wrap) l'espace d'un FBM à l'aide d'un FBM.
Ce serait le rêve dans le rêve d'Inception :
Le code suivant est une variation moins spectaculaire de cette technique, on utilise le wrapping (pliage, recouvrement, emballage) pour créer une sorte de nuage.
Notez la part que joue l'auto-similarité dans le résultat final :
<div class='codeAndCanvas' data='clouds.frag'></div>
Le fait de wrapper (plier, recouvrir, emballer) ainsi les coordonnées de textures peut être extrêmement utile, relativement amusant et terriblement dur à maîtriser.
C'est un outil puissant qui demande beaucoup de pratique pour être correctement utilisé.
"Flow noise", un célèbre article de Ken Perlin et Fabrice Neyret explique comment utiliser les dérivées (ou dégradés) du bruit pour atteindre ce résultat.
Les implémentations récentes du bruit de Perlin se servent à la fois de la fonction et de sa dérivée.
Si la vraie dérivée n'est pas disponible, on peut toujours se rabattre sur les différences finies pour l'approcher bien que ce soit moins précis et demande plus de travail.
---
13/README Id (13/README-id.md)
Gerak Pecahan Brownian
Noise cenderung memiliki arti yang berbeda bagi orang yang berbeda. Musisi akan menganggapnya dalam istilah suara yang mengganggu, komunikator sebagai interferensi, dan astrofisikawan sebagai radiasi latar gelombang mikro kosmik. Konsep-konsep ini membawa kita kembali ke alasan fisik di balik keacakan di dunia sekitar kita. Namun, mari kita mulai dengan sesuatu yang lebih mendasar, dan lebih sederhana: gelombang dan propertinya. Gelombang adalah fluktuasi dari waktu ke waktu pada beberapa properti. Gelombang audio adalah fluktuasi tekanan udara, gelombang elektromagnetik adalah fluktuasi medan listrik dan magnet. Dua karakteristik penting dari sebuah gelombang adalah amplitudo dan frekuensinya. Persamaan untuk gelombang linier sederhana (satu dimensi) terlihat seperti ini:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* Coba ubah nilai frekuensi dan amplitudo untuk memahami bagaimana perilakunya.
* Dengan menggunakan fungsi pembentukan, coba ubah amplitudo dari waktu ke waktu.
* Dengan menggunakan fungsi pembentukan, coba ubah frekuensi dari waktu ke waktu.
Dengan melakukan dua latihan terakhir Anda telah berhasil "memodulasi" gelombang sinus, dan Anda baru saja membuat gelombang AM (amplitudo termodulasi) dan FM (frekuensi termodulasi). Selamat!
Sifat lain yang menarik dari gelombang adalah kemampuannya untuk menjumlahkan, yang secara resmi disebut superposisi. Beri komentar/hapus komentar dan atur baris berikut. Perhatikan bagaimana tampilan keseluruhan berubah saat kita menambahkan gelombang dengan amplitudo dan frekuensi yang berbeda secara bersamaan.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01(-u_time130.0);
y += sin(xfrequency2.1 + t)*4.5;
y += sin(xfrequency1.72 + t1.121)4.0;
y += sin(xfrequency2.221 + t0.437)5.0;
y += sin(xfrequency3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Percobaan dengan mengubah frekuensi dan amplitudo untuk gelombang tambahan.
* Apakah mungkin membuat dua gelombang membatalkan satu sama lain? Akan terlihat seperti apa?
* Apakah mungkin menambahkan gelombang sedemikian rupa sehingga mereka akan memperkuat satu sama lain?
Dalam musik, setiap not dikaitkan dengan frekuensi tertentu. Frekuensi untuk nada-nada ini mengikuti pola yang kita sebut skala, di mana penggandaan atau separuh frekuensi sesuai dengan lompatan satu oktaf.
Sekarang, mari gunakan noise Perlin sebagai ganti gelombang sinus! Derau Perlin dalam bentuk dasarnya memiliki tampilan dan nuansa umum yang sama dengan gelombang sinus. Amplitudo dan frekuensinya agak bervariasi, tetapi amplitudonya tetap cukup konsisten, dan frekuensi dibatasi pada kisaran yang cukup sempit di sekitar frekuensi tengah. Ini tidak teratur seperti gelombang sinus, dan lebih mudah untuk membuat tampilan acak dengan merangkum beberapa versi skala noise. Dimungkinkan juga untuk membuat sejumlah gelombang sinus tampak acak juga, tetapi dibutuhkan banyak gelombang berbeda untuk menyembunyikan sifat berkala dan teraturnya.
Dengan menambahkan iterasi yang berbeda dari noise (oktaf), di mana kami secara berturut-turut meningkatkan frekuensi dalam langkah-langkah reguler (lacunaritas) dan menurunkan amplitudo (penguatan/gain) dari noise kita dapat memperoleh perincian yang lebih halus di noise dan dapatkan detail yang lebih halus. Teknik ini disebut "Gerak Pecahan Brownian" (GPB), atau sederhananya "gangguan fraktal", dan dalam bentuk yang paling sederhana dapat dibuat dengan kode berikut:
<div class="simpleFunction" data="// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Initial values
float amplitude = 0.5;
float frequency = 1.;
//
// Loop of octaves
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* Ubah jumlah oktaf secara bertahap untuk mengulang dari 1 ke 2, 4, 8 dan 10. Lihat apa yang terjadi.
* Jika Anda memiliki lebih dari 4 oktaf, coba ubah nilai lacunarity.
* Juga dengan> 4 oktaf, ubah nilai penguatan dan lihat apa yang terjadi.
Perhatikan bagaimana dengan setiap oktaf tambahan, kurva tampaknya menjadi lebih detail. Perhatikan juga kemiripan diri sementara lebih banyak oktaf ditambahkan. Jika Anda memperbesar kurva, bagian yang lebih kecil terlihat hampir sama dengan keseluruhannya, dan setiap bagian terlihat kurang lebih sama seperti bagian lainnya. Ini adalah properti penting dari fraktal matematika, dan kami mensimulasikan properti itu dalam loop kami. Kami tidak membuat fraktal benar, karena kami menghentikan penjumlahan setelah beberapa iterasi, tetapi secara teoritis, kami akan mendapatkan fraktal matematika yang sebenarnya jika kami membiarkan loop berlanjut selamanya dan menambahkan komponen noise dalam jumlah tak terbatas. Dalam grafik komputer, kita selalu memiliki batasan hingga detail terkecil yang dapat kita selesaikan, misalnya ketika objek menjadi lebih kecil dari piksel, jadi tidak perlu membuat jumlah tak terbatas untuk membuat tampilan fraktal. Terkadang banyak istilah mungkin diperlukan, tetapi tidak pernah dalam jumlah yang tak terbatas.
Kode berikut adalah contoh bagaimana GPB dapat diimplementasikan dalam dua dimensi untuk membuat pola yang tampak fraktal:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* Kurangi jumlah oktaf dengan mengubah nilai pada baris 37
* Modifikasi lacunarity dari GPB di baris 47
* Jelajahi dengan mengubah keuntungan di baris 48
Teknik ini biasanya digunakan untuk membangun lanskap prosedural. Kemiripan diri dari GPB sangat cocok untuk pegunungan, karena proses erosi yang menciptakan gunung bekerja dengan cara yang menghasilkan jenis kemiripan diri ini di berbagai skala. Jika Anda tertarik dengan penggunaan ini, Anda harus membaca artikel hebat ini oleh Inigo Quiles tentang noise tingkat lanjut.
Menggunakan teknik yang kurang lebih sama, juga memungkinkan untuk mendapatkan efek lain seperti yang dikenal sebagai turbulensi. Ini pada dasarnya adalah GPB, tetapi dibangun dari nilai absolut dari suara yang ditandatangani untuk menciptakan lembah yang tajam dalam fungsinya.
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Anggota lain dari kelompok algoritme ini adalah punggungan, di mana lembah tajam dibalik untuk membuat punggungan tajam:
glsln = abs(n); // create creases
n = offset - n; // invert so creases are at top
n = n * n; // sharpen creases
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Varian lain yang dapat membuat variasi yang berguna adalah dengan menggandakan komponen noise bersama-sama daripada menambahkannya. Menarik juga untuk menskalakan fungsi noise berikutnya dengan sesuatu yang bergantung pada istilah sebelumnya dalam loop. Ketika kita melakukan hal-hal seperti itu, kita menjauh dari definisi fraktal yang ketat dan menuju bidang "multifaktal" yang relatif tidak dikenal. Multifraktal tidak didefinisikan secara matematis secara ketat, tetapi itu tidak membuatnya kurang berguna untuk grafik. Faktanya, simulasi multifraktal sangat umum dalam perangkat lunak komersial modern untuk pembuatan medan. Untuk bacaan lebih lanjut, Anda dapat membaca bab 16 dari buku "Texturing and Modeling: a Prosedural Approach" (edisi ke-3), oleh Kenton Musgrave. Sayangnya, buku itu sudah tidak dicetak lagi sejak beberapa tahun lalu, tetapi Anda masih dapat menemukannya di perpustakaan dan di pasar barang bekas. (Ada versi PDF dari edisi pertama yang tersedia untuk dibeli secara online, tetapi jangan membelinya - ini hanya membuang-buang uang. Ini dari tahun 1994, dan tidak berisi barang pemodelan medan apa pun dari edisi ke-3.)
Warping Domain
Inigo Quiles menulis artikel menarik tentang bagaimana mungkin menggunakan GPB untuk membengkokkan ruang dari GPB. Pikiran bertiup, kan? Ini seperti mimpi di dalam mimpi Inception.
Contoh yang kurang ekstrem dari teknik ini adalah kode berikut di mana bungkus digunakan untuk menghasilkan tekstur seperti awan ini. Perhatikan bagaimana properti kemiripan diri tetap ada dalam hasil.
<div class='codeAndCanvas' data='clouds.frag'></div>
Warping tekstur koordinat dengan noise dengan cara ini bisa sangat berguna, sangat menyenangkan, dan sangat sulit untuk dikuasai. Ini adalah alat yang ampuh, tetapi membutuhkan sedikit pengalaman untuk menggunakannya dengan baik. Alat yang berguna untuk ini adalah menggeser koordinat dengan turunan (gradien) noise. Artikel terkenal oleh Ken Perlin dan Fabrice Neyret disebut "noise aliran" didasarkan pada gagasan ini. Beberapa implementasi modern noise Perlin menyertakan varian yang menghitung fungsi dan gradien analitiknya. Jika gradien "benar" tidak tersedia untuk fungsi prosedural, Anda selalu dapat menghitung perbedaan hingga untuk memperkirakannya, meskipun ini kurang akurat dan melibatkan lebih banyak pekerjaan.
---
13/README Jp (13/README-jp.md)
非整数ブラウン運動
ノイズという言葉は人によって違う物事を意味します。ミュージシャンであれば雑音のことを、コミュニケーションを仕事にする人であれば余計な干渉のことを、天体物理学者であれば宇宙マイクロ波背景放射のことを考えるでしょう。これらの概念は、世の中のランダムな現象の背後にある物理的な原因について考えさせてくれます。でも、まずはもっと根本的でシンプルなことから始めましょう。波とその特性についてです。波とは時間の上で起こる、ある特性を持った振動です。音波とは空気圧における振動、電磁波は電界、磁界における振動です。
波の2つ重要な性質に振幅(amplitude)と周波数(frequency)があります。単純な一次元の波を表す式はこのようになります。
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* 波の性質について理解を深めるため、振幅と周波数の値を変えてみましょう。
* シェイピング関数を使って振幅を時間とともに変化させてみましょう。
* シェイピング関数を使って周波数を時間とともに変化させてみましょう。
2番目と3番目の演習をすることであなたはサイン波を「モデュレート」させて、AM波(amplitude modulated)とFM波(frequency modulated)を作り出したことになります。
波の興味深い特性の1つに、重ね合わせができるということがあります。下記のコードをコメントアウトしたり数値を変えたりしてみましょう(訳注:
`y +=` で始まる行で試してください)。<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01 (-u_time130.0);
y += sin(xfrequency2.1 + t) * 4.5;
y += sin(xfrequency1.72 + t 1.121) 4.0;
y += sin(xfrequency2.221 + t 0.437) 5.0;
y += sin(xfrequency3.1122+ t 4.269) 2.5;
y = amplitude 0.06;
"></div>
* 重ね合わされている波の振幅や周波数を変えて実験してみましょう。
* 2つの波を互いに打ち消し合わせることはできますか。どのように見えるでしょう。
* 2つの波を、お互いを強め合うように重ね合わせることはできますか。
音楽では、それぞれの音程は特定の周波数に対応しています。これら音程の周波数は音階と呼ばれるパターンに従って並べられ、1オクターブごとに2倍または半分になります。
さて、サイン波の代わりにパーリンノイズを使ってみましょう。パーリンノイズの基本的な形状はサイン波(訳注:サイン波を重ね合わせたもの)に似た見た目をしています。振幅と周波数はいくらか変化しますが、振幅は比較的安定しており、周波数の変化はある値を中央値とした狭い範囲に制限されています。一方でパーリンノイズはサイン波ほど規則正しくはありません。幾つかのスケールを変えたノイズを重ね合わせればより不規則な見た目を作り出すのも簡単です。サイン波を重ね合わせたものをランダムに見せることも可能ですが、周期的で規則正しい性質を隠すにはたくさんの異なる波を重ねる必要があります。
周波数を一定の割合で増加させる(lacunarity)と同時に振幅を減らしながら(gain)ノイズを(octaveの数だけ)繰り返し重ねることで、より粒度の細かいディテールを持ったノイズを作り出すことができます。このテクニックは「非整数ブラウン運動(fBM)」または単に「フラクタルノイズ」と呼ばれていて、最も単純な形は下記のコードのようになります。
<div class="simpleFunction" data="// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Initial values
float amplitude = 0.5;
float frequency = 1.;
//
// Loop of octaves
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* 1から2、4、8、10と
`octave` の値を徐々に変化させて何が起きるか観察してみましょう。*
`octave` の値が4より大きい状態で `lacunarity` の値を変化させてみましょう。* 同じく
`octave` の値が4より大きい状態で `gain` の値を変化させてみましょう。`octave` を増やすと曲線のディテールがより細かくなることに注目してください。また、その際の自己相似性にも注意しましょう。曲線を拡大しても、細かな部分の形が全体の形と同じになっているように見えます。また、拡大した部分は多かれ少なかれ他の部分と似ています。これは数学的なフラクタルの重要な性質で、私たちはこの性質をループの中でシミュレートしているのです。ここではシミュレーションは数回の繰り返しの後で打ち切られているので本当の意味でのフラクタルを作り出しているわけではありません。しかし理論上では、ループを永遠に繰り返して無限にノイズを重ね合わせれば、本当に数学的なフラクタルを得ることができるでしょう。コンピュータグラフィクスでは常に処理できる細かさに限界があります。例えばピクセルより小さなものは見えないので、フラクタルな見た目を作り出すのに無限回の足し算を行う必要はありません。たくさんの繰り返しが必要になることはあるかもしれませんが、無限回になることは決してありません。下記のコードは2次元のfBMを実装してフラクタル状の模様を作り出した例です。
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* 37行目の
`octave` を減らしてみましょう。* 47行目の
`lacunarity` を変化させてみましょう。* 48行目の
`gain` を変えて試してみましょう。このテクニックはプログラムで地形を作り出すのによく用いられています。fBMの自己相似性は、山を形づくる侵食過程の自己相似性に似ているため、山脈の形を再現するのに最適です。もし興味があればInigo Quilesによる高度なノイズについての素晴らしい記事を読むと良いでしょう。
同様のテクニックを使って「乱流(turbulence)」と呼ばれる効果を作り出すこともできます。基本的にはfBMなのですが、鋭い谷間を作り出すために符号付きノイズ(signed noise)の絶対値を用いています。
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
同じ仲間のアルゴリズムに「尾根(ridge)」があります。深い谷が上下逆さまにされて鋭い尾根を作り出しています。
glsln = abs(n); // create creases
n = offset - n; // invert so creases are at top
n = n * n; // sharpen creases
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
他の便利な仲間には、ノイズを足し算する代わりに掛け合わせたものがあります。ノイズ関数をそれまでのループの結果に応じてスケールさせても面白いでしょう。こうすると厳密なフラクタルから離れて、より謎めいた「マルチフラクタル」の世界に足を踏み入れることになります。マルチフラクタルはフラクタルほどは数学的に厳密に定義されていませんが、だからと言って役に立たないというわけではありません(訳注:厳密に定義されてないというより、より一般化された概念なのでもっといろんなものを含んじゃうよ、という感じだと思います)。実際にマルチフラクタルシミュレーションは地形生成に用いる現代の商用ソフトで一般的に用いられています。より深く知りたければKenton Musgraveによる「Texturing and Modeling: a Procedural Approach (3rd edition)」を読むと良いでしょう。残念なことにこの本は数年前に絶版になってしまいましたが、図書館や中古で見つけることができるでしょう(第1版のPDF版はオンラインで手に入りますが買わないように。お金の無駄です。1994年に出版されたもので、第3版に載っている地形生成についての部分は含まれていません)。
ドメインワーピング
Inigo Quilesはこの興味をそそる記事でfBMを使ってfBMの空間を歪める方法について書いています。ぶっ飛んでますね。インセプションに出てくる夢の中の夢のようです。
下記はこのテクニックのもう少しおとなしいサンプルです。空間の歪みが雲のようなテクスチャを作り出すのに使われています。ここでもまた自己相似性が見られることに注目してください。
<div class='codeAndCanvas' data='clouds.frag'></div>
このようにテクスチャ座標を歪める技はとても便利で、楽しく、しかし非道なほどマスターするのが難しいものです。とても強力なツールですが使いこなすにはかなりの経験が必要です。便利なテクニックとしてノイズの微分(勾配)を用いて座標をずらす方法があります。Ken PerlinとFabrice Neyretによる「flow noise 」という記事はこのアイデアに基づくものです。最近のパーリンノイズの実装の幾つかは、その関数の値と解析的な勾配の両方を計算してくれます。もしある関数について本物の勾配が得られない場合でも常に、手間がかかり正確さに劣りますが、差分法を用いて近似することができます。
(訳注:要するにノイズの値そのものではなくその微分を使うという話です。微分が直接計算できない場合は微妙にずらした2点の値を計算して差分をとることで近似することができます。この辺りはもう少し説明が必要ですね。参考になるかわかりませんがこのサンプルでは
`normal` 関数の中で少し座標をずらしてノイズの値を計算して比較することで傾きを計算しています。)---
13/README Pl (13/README-pl.md)
Fractal Brownian Motion (pol. "fraktalne ruchy Browna")
Szum zwykle oznacza różne rzeczy dla różnych ludzi. Muzycy będą myśleć o nim w kategoriach przeszkadzających dźwięków, komunikatorzy jako o zakłóceniach, a astrofizycy jako o kosmicznym mikrofalowym promieniowaniu tła. Te koncepcje sprowadzają nas z powrotem do fizycznych przyczyn losowości w otaczającym nas świecie. Zacznijmy jednak od czegoś bardziej podstawowego i prostszego: od fal i ich właściwości. Fala jest fluktuacją w czasie jakiejś właściwości. Fale dźwiękowe to fluktuacje ciśnienia powietrza, fale elektromagnetyczne to fluktuacje pola elektrycznego i magnetycznego. Dwie ważne cechy fali to jej amplituda i częstotliwość. Równanie dla prostej liniowej (jednowymiarowej) fali wygląda tak:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* Spróbuj zmienić wartości częstotliwości i amplitudy, aby zrozumieć, jak się zachowują.
* Używając shaping functions, spróbuj zmienić amplitudę w czasie.
* Używając shaping functions, spróbuj zmienić częstotliwość w czasie.
Wykonując dwa ostatnie ćwiczenia udało Ci się "zmodulować" sinusoidę i właśnie stworzyłeś fale AM (modulowane amplitudą, ang. "amplitude modulated") i FM (modulowane częstotliwością, ang. "frequency modulated"). Gratulacje!!!
Inną ciekawą właściwością fal jest ich zdolność do sumowania się, co formalnie nazywa się superpozycją. Skomentuj/odkomentuj i zmodyfikuj poniższe linijki. Zwróć uwagę, jak zmienia się ogólny wygląd wykresu, gdy dodajemy do siebie fale o różnych amplitudach i częstotliwościach.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01(-u_time130.0);
y += sin(xfrequency2.1 + t)*4.5;
y += sin(xfrequency1.72 + t1.121)4.0;
y += sin(xfrequency2.221 + t0.437)5.0;
y += sin(xfrequency3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Eksperymentuj, zmieniając częstotliwość i amplitudę dla dodatkowych fal.
* Czy jest możliwe, aby dwie fale wzajemnie się zniosły? Jak to będzie wyglądało?
* Czy można dodać fale w taki sposób, że będą się one wzajemnie wzmacniać?
W muzyce każda nuta jest związana z określoną częstotliwością. Układ częstotliwości tych nut nazywamy skalą, gdzie podwojenie lub zmniejszenie częstotliwości o połowę odpowiada skokowi o jedną oktawę.
Teraz użyjmy szumu Perlina zamiast sinusoidy! Szum Perlina w swojej podstawowej formie wygląda podobnie do sinusoidy. Jego amplituda i częstotliwość różnią się nieco, ale amplituda pozostaje w miarę stała, a częstotliwość jest ograniczona do dość wąskiego zakresu wokół częstotliwości środkowej. Szum nie jest jednak tak regularny jak sinusoida, tym bardziej, gdy zsumujemy jego kilka przeskalowanych wersji. Można sprawić, że suma fal sinusoidalnych również będzie wyglądać na przypadkową, ale potrzeba wielu różnych fal, aby ukryć ich okresową, regularną naturę.
Poprzez dodanie różnych iteracji szumu (octaves, pol. "oktawy"), gdzie kolejno zwiększamy częstotliwości w regularnych krokach (lacunarity, pol. "lakunarność") i zmniejszamy amplitudę (gain, pol. "wzmocnienie"), otrzymamy szum bardziej granularny, zawierający więcej detali. Technikę tę nazwywamy "fractal Brownian Motion" (fBM) lub, po prostu "fractal noise" (pol. "szum fraktalny"). W swojej najprostszej postaci, możemy go stworzyć w następujący sposób:
<div class="simpleFunction" data="// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Początkowe wartości
float amplitude = 0.5;
float frequency = 1.;
//
// Pętla po oktawach
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* Stopniowo zmieniaj liczbę oktaw z 1 do 2, 4, 8 i 10. Zobacz, co się stanie.
* Gdy masz więcej niż 4 oktawy, spróbuj zmienić wartość
lacunarity.
* Również przy >4 oktawach zmień wartość gain i zobacz, co się stanie.Zauważ, że z każdą dodatkową oktawą krzywa wydaje się być bardziej szczegółowa. Zauważ też, że w miarę dodawania kolejnych oktaw występuje efekt samopodobieństwa - jeśli powiększysz krzywą, powiększona część wygląda mniej więcej tak samo jak całość, a każda powiększona część wygląda mniej więcej tak samo jak każda inna. Jest to ważna właściwość fraktali matematycznych, a my symulujemy tę właściwość w naszej pętli. Nie tworzymy prawdziwego fraktala, ponieważ zatrzymujemy sumowanie po kilku iteracjach, ale teoretycznie rzecz biorąc, uzyskalibyśmy prawdziwy fraktal matematyczny, gdybyśmy pozwolili pętli trwać w nieskończoność i dodawali nieskończoną liczbę składowych szumu. W grafice komputerowej zawsze mamy limit najmniejszych szczegółów, które możemy wyrenderować, gdyż obiekty stają się mniejsze niż piksel, więc nie ma potrzeby wykonywania nieskończonych sum, aby stworzyć wygląd fraktala. Czasami może być potrzebna duża ilość iteracji, ale nigdy nieskończona liczba.
Poniższy kod jest przykładem tego, jak fBm może być zaimplementowany w dwóch wymiarach, aby stworzyć wzór wyglądający jak fraktal:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* Zmniejsz liczbę oktaw poprzez zmianę wartości w linii 37
* Zmodyfikuj
lacunarity fBm w linii 47
* Eksploruj zmianę gain na linii 48Ta technika jest powszechnie używana do konstruowania proceduralnych krajobrazów. Samopodobieństwo fBm jest idealne dla gór, ponieważ procesy erozji, które tworzą góry, działają w sposób, który daje ten rodzaj samopodobieństwa w dużym zakresie skal. Jeśli jesteś zainteresowany tym zastosowaniem, powinieneś koniecznie przeczytać ten świetny artykuł Inigo Quilesa o zaawansowanym szumie.
Używając mniej więcej tej samej techniki, możliwe jest również uzyskanie innych efektów, takich jak turbulencja. Jest to w zasadzie fBm, ale skonstruowane z wartości bezwzględnej szumu (wariantu zwracającego również ujemne wartości), aby stworzyć ostre doliny w funkcji.
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Innym członkiem tej rodziny algorytmów jest ridge (pol. "grzbiet"), w którym ostre doliny są odwrócone do góry nogami, tworząc zamiast nich ostre grzbiety:
glsln = abs(n); // Stwórz doliny
n = offset - n; // Odwróć doliny, aby powstały grzbiety
n = n * n; // Zaostrz grzbiety
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Innym użytecznym wariantem jest mnożenie składowych szumu zamiast ich dodawania. Interesujące jest również skalowanie kolejnych funkcji szumu za pomocą czegoś, co zależy od poprzednich terminów w pętli. Kiedy robimy takie rzeczy, odchodzimy od ścisłej definicji fraktala i wchodzimy w stosunkowo nieznaną dziedzinę "multifraktali". Multifraktale nie są tak ściśle zdefiniowane matematycznie, ale to nie czyni ich mniej użytecznymi dla grafiki. W rzeczywistości symulacje multifraktalne są bardzo powszechne we współczesnym komercyjnym oprogramowaniu do generowania terenu. Aby przeczytać więcej, możesz przeczytać rozdział 16 książki "Texturing and Modeling: a Procedural Approach" (3. edycja), autorstwa Kentona Musgrave. Niestety, książka ta jest już od kilku lat niedostępna w druku, ale wciąż można ją znaleźć w bibliotekach i na rynku wtórnym. (Istnieje wersja PDF pierwszego wydania dostępna do kupienia online, ale nie kupuj jej - to strata pieniędzy. Jest z 1994 roku i nie zawiera żadnych rzeczy związanych z modelowaniem terenu z 3. edycji).
Warping (pol. "zakrzywianie")
Inigo Quiles napisał też inny fascynujący artykuł o tym jak można użyć fBm do zakrzywienia przestrzeni fBm. Zdumiewające, prawda? To jak sen wewnątrz snu o Incepcji.
Mniej ekstremalnym przykładem tej techniki jest następujący kod, w którym zakrzywienie jest używane do wytworzenia tekstury przypominającej chmury. Zauważ, że właściwość samopodobieństwa jest nadal obecna.
<div class='codeAndCanvas' data='clouds.frag'></div>
Zakrzywianie współrzędnych tekstury za pomocą szumu może być bardzo użyteczne, daje dużo frajdy, ale jest diabelnie trudne do opanowania. Jest to potężne narzędzie, ale potrzeba sporo doświadczenia, aby dobrze je wykorzystać. Przydatnym wariantem jest też przemieszczanie współrzędnych za pomocą pochodnej (gradientu) szumu. Na tym pomyśle opiera się słynny artykuł Kena Perlina i Fabrice'a Neyreta o nazwie "flow noise". Niektóre nowoczesne implementacje szumu Perlina zawierają wariant, który oblicza zarówno funkcję, jak i jej gradient. Jeśli gradient nie istnieje, zawsze możesz obliczyć skończone różnice (różnica między sąsiadującymi pikselami), aby go przybliżyć, chociaż jest to mniej dokładne i wymaga więcej pracy.
---
13/README Pt (13/README-pt.md)
Movimento Browniano Fracionário
O ruído pode ter um significado diferente para diferentes pessoas. Músicos pensarão em sons irritantes, comunicadores como interferência e astrofísicos como radiação em micro-ondas cósmicas. Estes conceitos nos trazem às explicações físicas por trás da aleatoriedade do mundo em nossa volta. Entretanto, vamos começar por algo mais fundamental e simples: ondas e suas propriedades. Uma onda é uma flutuação de alguma propriedade pelo tempo. Ondas de som são flutuações sobre a pressão do ar, ondas eletromagnéticas são flutuações em campos elétricos e magnéticos. Duas importantes características de onda são sua amplitude e sua frequência. A equação para uma simples onda linear (unidimensional) tem este aspecto.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* Experimente alterar os valores de frequência e amplitude para entender como eles se comportam.
* Usando modelagem de funções, tente alterar a amplitude ao longo do tempo.
* Usando modelagem de funções, tente alterar a frequência ao longo do tempo.
Ao fazer estes últimos exercícios, você conseguiu "modular" uma onda de seno, e você acabou de criar ondas AM (amplitude modulada) e FM (frequência modulada). Parabéns!
Outra interessante propriedade das ondas é a habilidade de se somarem, formalmente chamada de superposição. Comente/descomente e ajuste as linhas a seguir. Preste atenção em como sua aparência em geral se altera conforme nós adicionamos ondas de diferentes amplitudes e frequências juntas.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01(-u_time130.0);
y += sin(xfrequency2.1 + t)*4.5;
y += sin(xfrequency1.72 + t1.121)4.0;
y += sin(xfrequency2.221 + t0.437)5.0;
y += sin(xfrequency3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Experimente alterar a frequência e amplitude das ondas adicionais.
* É possível fazer com que duas ondas se cancelem?
* É possível adicionar ondas de uma forma que elas possam se amplificar?
Em música, cada nota é associada com uma frequência específica. As frequências para estas notas seguem um padrão que chamamos de escala, onde dobrando ou diminuindo pela metade, a frequência corresponde a um salto de uma oitava.
Agora, vamos usar o ruído Perlin em vez de uma onda de seno! Ruído Perlin em sua forma básica se assemelha muito a uma onda de seno. Sua amplitude e frequência variam, mas a amplitude se mantém razoavelmente consistente, e a frequência é restrita a um curto intervalo ao redor da frequência central. Não é uniforme como uma onda de seno, no entanto, e é mais fácil de criar uma aparência de aleatoriedade ao somar diversas versões em diferentes escalas. Também possível criar uma soma de ondas de seno se parecerem com aleatório, mas é preciso muitas ondas diferentes para esconder a sua natureza periódica e regular.
Ao adicionar diferentes iterações ao ruído (oitavas), onde nós sucessivamente incrementamos as frequências em passos regulares (lacunaridade) e diminuímos a amplitude (ganho) do ruído, nós podemos obter uma granularidade no ruídos e ganhando detalhes mais finos. Esta técnica é chamada "Movimento browniano fracionário" (fBM), ou simplesmente "ruído fractal", sua forma mais simples pode ser criada com o seguinte código:
<div class="simpleFunction" data="// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Initial values
float amplitude = 0.5;
float frequency = 1.;
//
// Loop of octaves
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* Progressivamente mude o número de oitavas para iterar de 1 para 2, 4, 8 e 10. Veja o que acontece.
* Quando você ter mais do que 4 oitavas, tente mudar o valor de lacunaridade.
* Também, com mais de 4 oitavas, mude o valor gain(ganho) e veja o que acontece.
Veja como cada oitava adicional, a curva se parece mais detalhada. Também veja que a auto-similaridade enquanto mais oitavas são adicionadas. Se você se aproximar da curva, uma parte pequena se parecerá com o todo, e cada seção se parece mais ou menos igual a qualquer outra seção. Esta é uma importante propriedade dos fractais matemáticos, e nós estamos simulando esta propriedade em nosso loop. Nós não estamos criando um verdadeiro fractal, porque nós pararíamos nossa simulação depois de algumas iterações, mas, teoricamente falando, nós obteríamos um verdadeiro fractal se nós permitíssemos que o laço continuasse para sempre adicionando um infinito número de ruídos componentes. Em computação gráfica, nós sempre temos um limite do que nós podemos determinar, por exemplo, quando um objeto se torna menor que um píxel, então não há necessidade de criar somas infinitas para criar a aparência de um fractal. As vezes, muitos termos podem ser necessários, mas nunca um número infinito.
O código a seguir é um exemplo de como fBm pode ser implementado em duas dimensões para criar um padrão parecido com um fractal:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* Reduza o número de oitavas ao trocar os valores na linha 37
* Modifique a lacunaridade do fBm na linha 47
* Experimente mudar o Ganho(gain) na linha 48.
Esta técnica é regularmente usada na criação de paisagens procedimentais. A auto-similaridade do fBm é perfeita para montanhas, porque o processo de erosão cria montanhas que funcionam de um jeito que produzem esse tipo de auto-similaridade por um largo intervalo de escalas. Se você está interessado nisso, você definitivamente deveria ler este ótimo artigo por Inigo Quilez sobre ruído avançado.
Usando mais ou menos a mesma técnica, é possível obter outros efeitos como o que é conhecido como turbulência. É essencialmente um fBm, mas construída com valores absolutos de um ruído sinalizado para criar vales afiados na função.
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Outro membro desta família de algoritmos é o cume (ridge), onde os vales afiados são virados de ponta-cabeça para criar cumes afiados.
glsln = abs(n); // create creases
n = offset - n; // invert so creases are at top
n = n * n; // sharpen creases
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Outra variante pode criar variações úteis é multiplicando os componentes do ruído juntos ao invés de somá-los. Também é interessante alterarmos a escala de funções de ruído subsequentes com algo que dependa dos termos anteriores do nosso laço. Quando nós fazemos operações assim, nós estando nos afastando de uma definição estrita de fractal e entrando relativamente em um campo desconhecido de "multifractais". Multifractais não são estritamente matematicamente definidos, mas não significa que que os torna menos úteis em gráficos. De fato, simulações multifractais são muito comuns em comerciais modernos para gerar terrenos. Para ir além, você pode ler o capítulo 16 do livro "Texturing and Modeling: A procedural approach" (Texturizando e Modelando: Uma Abordagem Procedimental) - 3ª edição, por Kenton Musgrave. Infelizmente, este livro está fora de impressão há alguns anos, mas nós ainda podemos encontrá-lo em bibliotecas e em mercados de segunda-mão. (Tem uma versão em PDF da primeira edição disponível para venda online, mas não a compre - É um desperdício de dinheiro. É de 1994, e não contém o tópico de modelagem de terreno da terceira edição).
Domain Warping (Empenamento de Domínio)
Inigo Quilez escreveu outro artigo fascinante sobre como é possível usarmos o fBm para distorcer um espaço de um fBm. Surpreendente, não é? É como um sonho dentro de um sonho em A Origem (Inception).
Uma exemplo menos extremo desta técnica está nas seguintes linhas de código, onde o empenamento é usado para produzir estas texturas parecidas com nuvem. Veja como a propriedade de auto-similaridade está presente no resultado.
<div class='codeAndCanvas' data='clouds.frag'></div>
Empenar as coordenadas da textura com um ruído pode ser muito útil e divertido, entretanto, diabolicamente difícil de dominar. É uma ferramenta poderosa, mas requer um pouco de experiência pra usá-la bem. Uma ferramenta útil para isso é deslocar as coordenadas com as derivadas (gradiente) do ruído. Um artigo famoso de Ken Perlin e Fabrice Neyret chamado "flow noise" é baseado nesta ideia. Algumas implementações modernas do ruído Perlin incluem a variante que calcula tanto a função como seu gradiente analítico. Se o "verdadeiro" gradiente não estiver disponível para nossa função procedimental, você sempre pode calcular as diferenças finitas para se aproximar dele, apesar de ser menos preciso e envolver mais trabalho.
---
13/README Tr (13/README-tr.md)
Fraktal Brown Hareketi
Gürültü, farklı insanlar için farklı şeyler ifade eder. Müzisyenler onu rahatsız edici sesler açısından düşünür, iletişimciler parazit olarak ve astrofizikçiler kozmik mikrodalga arka plan radyasyonu olarak. Bu kavramlar bizi çevremizdeki dünyadaki rastgeleliğin arkasındaki fiziksel nedenlere geri götürür. Ancak daha temel ve daha basit bir şeyle başlayalım: dalgalar ve özellikleri. Dalga, bazı özelliklerin zaman içindeki dalgalanmasıdır. Ses dalgaları hava basıncındaki dalgalanmalardır, elektromanyetik dalgalar elektrik ve manyetik alanlardaki dalgalanmalardır. Bir dalganın iki önemli özelliği genliği ve frekansıdır. Basit bir doğrusal (tek boyutlu) dalganın denklemi şöyle görünür:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* Nasıl davrandıklarını anlamak için frekans ve genlik değerlerini değiştirmeyi deneyin.
* Şekillendirme fonksiyonlarını kullanarak genliği zamanla değiştirmeyi deneyin.
* Şekillendirme fonksiyonlarını kullanarak frekansı zamanla değiştirmeyi deneyin.
Son iki alıştırmayı yaparak bir sinüs dalgasını "modüle etmeyi" başardınız ve az önce AM (genlik modülasyonlu) ve FM (frekans modülasyonlu) dalgalar yarattınız. Tebrikler!
Dalgaların bir diğer ilginç özelliği, resmi olarak süperpozisyon olarak adlandırılan, toplanabilme yetenekleridir. Aşağıdaki satırları yorum yapın/yorumdan çıkarın ve ayarlayın. Farklı genliklere ve frekanslara sahip dalgaları bir araya getirdikçe genel görünümün nasıl değiştiğine dikkat edin.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01(-u_time130.0);
y += sin(xfrequency2.1 + t)*4.5;
y += sin(xfrequency1.72 + t1.121)4.0;
y += sin(xfrequency2.221 + t0.437)5.0;
y += sin(xfrequency3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Ek dalgalar için frekans ve genliği değiştirerek denemeler yapın.
* İki dalganın birbirini iptal etmesi mümkün mü? Bu nasıl görünür?
* Dalgaları birbirini güçlendirecek şekilde eklemek mümkün mü?
Müzikte her nota belirli bir frekansla ilişkilendirilir. Bu notaların frekansları, gam adını verdiğimiz bir deseni izler; burada frekansın iki katına çıkması veya yarıya inmesi bir oktav atlamaya karşılık gelir.
Şimdi sinüs dalgası yerine Perlin gürültüsünü kullanalım! Perlin gürültüsü temel formunda sinüs dalgasıyla aynı genel görünüm ve histe sahiptir. Genliği ve frekansı bir ölçüde değişir, ancak genlik makul ölçüde tutarlı kalır ve frekans bir merkez frekans etrafında oldukça dar bir aralıkla sınırlıdır. Bir sinüs dalgası kadar düzenli değildir ve gürültünün birkaç ölçeklenmiş versiyonunu toplayarak rastgelelik görünümü oluşturmak daha kolaydır. Sinüs dalgalarının toplamını da rastgele göstermek mümkündür, ancak periyodik, düzenli doğalarını gizlemek için birçok farklı dalga gerekir.
Frekansları düzenli adımlarla (lacunarity) art arda artırdığımız ve gürültünün genliğini (gain) azalttığımız, gürültünün farklı yinelemelerini (oktavlar) ekleyerek daha ince bir ayrıntı düzeyi elde edebilir ve daha ince detaylar alabiliriz. Bu teknik "fraktal Brown hareketi" (fBM) veya basitçe "fraktal gürültüsü" olarak adlandırılır ve en basit formunda aşağıdaki kodla oluşturulabilir:
<div class="simpleFunction" data="// Özellikler
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Başlangıç değerleri
float amplitude = 0.5;
float frequency = 1.;
//
// Oktav döngüsü
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* Yinelenecek oktav sayısını 1'den 2, 4, 8 ve 10'a kademeli olarak değiştirin. Ne olduğunu görün.
* 4'ten fazla oktavınız olduğunda, lacunarity değerini değiştirmeyi deneyin.
* Yine 4'ten fazla oktavla, gain değerini değiştirin ve ne olduğunu görün.
Her ek oktavla eğrinin nasıl daha fazla detay kazandığına dikkat edin. Ayrıca daha fazla oktav eklendikçe öz-benzerliğe de dikkat edin. Eğriye yakınlaştırırsanız, daha küçük bir kısım bütünle aşağı yukarı aynı görünür ve her bölüm diğer herhangi bir bölümle aşağı yukarı aynı görünür. Bu, matematiksel fraktalların önemli bir özelliğidir ve biz döngümüzde bu özelliği simüle ediyoruz.
Aşağıdaki kod, fraktal görünümlü bir desen oluşturmak için fBm'nin iki boyutta nasıl uygulanabileceğinin bir örneğidir:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* 37. satırdaki değeri değiştirerek oktav sayısını azaltın
* 47. satırda fBm'nin lacunarity'sini değiştirin
* 48. satırda gain'i değiştirerek keşfedin
Bu teknik genellikle prosedürel manzaralar oluşturmak için kullanılır. fBm'nin öz-benzerliği dağlar için mükemmeldir, çünkü dağları oluşturan erozyon süreçleri geniş bir ölçek aralığında bu tür bir öz-benzerlik veren bir şekilde çalışır. Bu kullanımla ilgileniyorsanız, Inigo Quiles'in gelişmiş gürültü hakkındaki bu harika makalesini kesinlikle okumalısınız.
Aşağı yukarı aynı tekniği kullanarak türbülans olarak bilinen başka efektler elde etmek de mümkündür. Özünde bir fBm'dir, ancak fonksiyonda keskin vadiler oluşturmak üzere işaretli gürültünün mutlak değerinden oluşturulmuştur.
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Bu algoritma ailesinin bir diğer üyesi, keskin vadilerin bunun yerine keskin sırtlar oluşturmak üzere ters çevrildiği sırttır (ridge):
glsln = abs(n); // kıvrımlar oluştur
n = offset - n; // kıvrımları üstte olacak şekilde ters çevir
n = n * n; // kıvrımları keskinleştir
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Yararlı varyasyonlar oluşturabilecek bir diğer değişken, gürültü bileşenlerini toplamak yerine çarpmaktır. Sonraki gürültü fonksiyonlarını döngüdeki önceki terimlere bağlı bir şeyle ölçeklemek de ilginçtir. Böyle şeyler yaptığımızda, fraktalın katı tanımından uzaklaşıp nispeten bilinmeyen "multifraktal" alanına geçiyoruz.
Alan Bükme (Domain Warping)
Inigo Quiles bu diğer büyüleyici makaleyi yazdı, bir fBm'nin uzayını bir fBm ile bükmenin nasıl mümkün olduğu hakkında. Akıl patlatıcı, değil mi? Inception'daki rüyanın içindeki rüya gibi.
Bu tekniğin daha az aşırı bir örneği, bulutlara benzer doku üretmek için kaydırmanın (wrap) kullanıldığı aşağıdaki koddur. Sonuçta öz-benzerlik özelliğinin hâlâ mevcut olduğuna dikkat edin.
<div class='codeAndCanvas' data='clouds.frag'></div>
Doku koordinatlarını bu şekilde gürültüyle kaydırmak çok faydalı, çok eğlenceli ve ustalaşması çok zor olabilir. Güçlü bir araçtır, ancak iyi kullanmak için oldukça fazla deneyim gerektirir. Bunun için faydalı bir araç, koordinatları gürültünün türevi (gradyanı) ile kaydırmaktır. Ken Perlin ve Fabrice Neyret tarafından yazılan "flow noise" başlıklı ünlü makale bu fikre dayanır.
#### Araç kutunuz için
* LYGIA'nın üretken fonksiyonları, GLSL'de desenler üretmek için yeniden kullanılabilir fonksiyonlar kümesidir. Üretken sanat yaratmak için rastgelelik ve gürültüyü nasıl kullanacağınızı öğrenmek için harika bir kaynaktır. Yeniden kullanılabilirlik, performans ve esneklik için tasarlanmış çok ayrıntılı bir kütüphanedir. Ve herhangi bir projeye ve framework'e kolayca eklenebilir.
---
13/README Vi (13/README-vi.md)
Chuyển động Brown (Fractal Brownian motion)
Nói về nhiễu, mỗi người sẽ có một suy nghĩ khác nhau. Các nhạc công sẽ nghĩ về những tiếng ồn khó chịu, các nhà vật lý thiên văn học thì nghĩ tới bức xạ nền vi sóng vũ trụ, còn trong giao tiếp thì nhiễu dùng để chỉ các yếu tố gây mất tập trung. Các khái niệm này đưa chúng ta trở lại với môn Vật lý phổ thông để giải thích những tính chất đằng sau sự ngẫu nhiên. Tuy nhiên, hãy bắt đầu với thứ gì đó cơ bản hơn: Sóng và các tính chất của nó. Một sóng sự dao động của một vài thuộc tính vật lý theo thời gian. Sóng âm là sự dao động của áp suất không khí, sóng điện từ là dao động trong điện trường và từ trường. Hai tính chất quan trọng của sóng là biên độ và tần số. Phương trình sóng tuyến tính (một chiều) nhìn như sau:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude sin(x frequency);
"></div>
* Hãy thử thay đổi giá trị của biên độ và tần số để xem đồ thị thay đổi tương ứng như thế nào.
* Thay đổi biên độ theo thời gian bằng các hàm hình dạng
* Thay đổi tần số theo thời gian bằng các hàm hình dạng
Người dịch: Nếu mỗi khi tăng tần số bạn lại giảm biên độ một đại lượng tương ứng, bạn sẽ thấy "một phiên bản thu nhỏ" của sóng ban đầu, như đoạn code dưới đây. Bạn có thể dùng GraphToy để vẽ từng sóng này rời nhau, bạn sẽ thấy rõ hơn. Các họa tiết nhìn giống nhau chỉ khác về kích thước như vậy được gọi là Fractal.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
float scale = 8.; // sửa dòng này để thấy từng cấp của pattern Fractal
y = amplitude 1. / scale sin(x frequency scale);
"></div>
Hai yêu cầu cuối sẽ giúp bạn thuần thục hơn trong kỹ năng kiểm soát (modulate) sóng sin, và hai sóng mà bạn vừa tạo ra có tên gọi riêng, đó là AM (Amplitude Modulated) và FM (Frequency Modulated). Chúc mừng nhé !
Một đặc điểm khác cũng thú vị không kém của các sóng là chúng có thể cộng dồn và chồng chất lên nhau. Hãy comment/Uncomment và sửa các dòng code sau và chú ý vào sự thay đổi của đồ thị khi ta chồng các sóng có biên độ và tần số khác nhau.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01(-u_time130.0);
y += sin(xfrequency2.1 + t)*4.5;
y += sin(xfrequency1.72 + t1.121)4.0;
y += sin(xfrequency2.221 + t0.437)5.0;
y += sin(xfrequency3.1122+ t4.269)2.5;
y = amplitude0.06;
"></div>
* Hãy thử thêm bớt từng sóng.
* Có thể tạo hai sóng triệt tiêu lẫn nhau không ? Nếu có thì trông chúng như thế nào ?
* Có thể tạo hai sóng hoàn toàn khuếch đại lẫn nhau không ? Nếu có thì trông chúng như thế nào ?
Trong âm nhạc, mỗi nốt nhạc tương ứng với một tần số. Những tần số này tuân theo một pattern mà ta gọi là gam / âm giai, khi tăng gấp đôi hoặc giảm một nửa tần số ta được các nốt nhạc ở một quãng tám ngay trên và dưới các nốt ban đầu.
Giờ hãy dùng Perlin noise thay vì sóng sin. Perlin noise ở dạng cơ bản nhất nhìn cũng na ná sóng sin. Dù cho biên độ và tần số của nó có sai số đi nữa, biên độ vẫn có mức ổn định chấp nhận được còn tần số thì bị giới hạn để chỉ dao động quanh mốc tần số trung tâm. Tuy không phổ biến như sóng sin, nhưng, nếu chồng nhiều sóng nhiễu lên ta có thể tạo nên sự ngẫu nhiên mong muốn. Còn nếu chồng các sóng sin lên nhau mà vẫn muốn tạo ra sự ngẫu nhiên thì sẽ tốn khá nhiều công để che giấu tần số.
Thay vì dùng các sóng nhiễu giống hệt nhau, ta sẽ dùng các cấp fractal của sóng. Cứ mỗi lần chồng thêm một sóng nhiễu (tức thêm một quãng tám - octaves), ta sẽ tăng tần số bằng cách nhân với một đại lượng cố định (lacunarity) và giảm biên độ cũng bằng cách nhân với một đại lượng cố định (gain), kết quả thu được là sóng nhiễu ngày càng chi tiết hơn. Kỹ thuật này gọi là chuyển động Brown - "fractal Brownian Motion" (fBm), hoặc đơn giản là nhiễu phân dạng - "fractal noise", có thể sinh bởi đoạn code sau:
<div class="simpleFunction" data="// Các thuộc tính
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Giá trị ban đầu
float amplitude = 0.5;
float frequency = 1.;
//
// Vòng lặp các quãng tám
for (int i = 0; i < octaves; i++) {
	y += amplitude noise(frequencyx);
	frequency *= lacunarity;
	amplitude *= gain;
}"></div>
* Hãy thử thay đổi giá trị của quãng tám từ 1 lên 2, 4, 8, 10 và xem đồ thị thay đổi ra sao.
* Với số quãng tám lớn hơn 4, hãy thử thay đổi giá trị lacunarity.
* Cũng với số quãng tám lớn hơn 4, hãy thử thay đổi giá trị gain.
Bạn sẽ dễ dàng nhận ra cứ chồng thêm một quãng tám, đồ thị lại càng chi tiết hơn. Mỗi cấp sóng nhiễu được thêm vào lại giống như một phiên bản tí hon của cấp trước đó. Đó là một tính chất quan trọng của phân dạng (fractal) trong Toán học. Thực ra thì ta không tạo ra một phân dạng thực thụ, vì số vòng lặp giới hạn, nhưng về lý thuyết thì điều đó có thể với một vòng lặp vô hạn. Trong lĩnh vực đồ họa máy tính thì ta luôn phải đánh đổi sự chi tiết với số lượng phép tính cần thực hiện. Trong trường hợp này, nếu sự thay đổi của đồ thị nhỏ hơn kích thước của một điểm ảnh thì việc tiếp tục chồng thêm quãng tám là không cần thiết nữa.
Dưới đây là ví dụ tôi dùng fBm trong không gian 2 chiều để tạo nên họa tiết phân dạng:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* Hãy thử giảm số quãng tám ở dòng 37
* Hãy thử sửa tham số lacunarity ở dòng 47
* Hãy thử sửa tham số gain ở dòng 48
Kỹ thuật này rất hay được sử dụng để tạo nên các địa hình ngẫu nhiên. Tính chất tương tự của các cấp fractal trong fBm vô cùng thích hợp để mô tả các ngọn núi, vì quá trình ăn mòn để tạo nên các ngọn núi cũng được mô phỏng bằng chính đặc tính này. Nếu bạn thích chủ đề này, tôi đặc biệt giới thiệu nghiên cứu tuyệt vời này của Inigo Quiles về nhiễu cao cấp.
Sử dụng các kỹ thuật tương tự, ta có thể tạo nên hiệu ứng nhiễu loạn - turbulence. Thực ra nó chính là fBm nhưng sử dụng các giá trị tuyệt đối của nhiễu để tạo nên các hố sâu, gọi là thung lũng.
glslfor (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Một thành viên khác trong họ thuật toán này là dãy núi - ridge, chỉ khác là thay vì có các thung lũng sâu, ta có các dãy núi dựng đứng:
glsln = abs(n); // tạo các rãnh thung lũng
n = offset - n; // đảo ngược thung lũng thành đỉnh núi
n = n * n; // tăng góc nghiêng của các đỉnh núi
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Một biến thể khác để tạo ra các sóng nhiễu đa dạng hơn, đó là nhân giá trị của các vị trí tương ứng trên các sóng nhiễu lại với nhau, thay vì cộng dồn. Và các cấp sóng fractal có thể được scale dựa vào cấp trước đó, những sóng kết quả sẽ được gọi là "multifractal". Tuy các sóng "multifractal" không chặt chẽ về mặt Toán học lắm, nhưng điều đó không làm giảm tính hữu dụng của chúng trong đồ họa máy tính. Thực tế thì chúng rất hay được sử dụng ở các phần mềm thương mại giúp sinh ra các loại địa hình khác nhau. Để tìm hiểu thêm, bạn có thể đọc chương 16 của quyển "Texturing and Modeling: a Procedural Approach" (lần xuất bản thứ 3), được viết bởi Kenton Musgrave. Rất tiếc là bản in của quyển sách này đã hết sạch từ vài năm trước rồi, nhưng bạn vẫn có thể tìm mượn ở thư viện hoặc mua lại sách cũ. (Tuy rằng có phiên bản PDF được rao bán trên mạng nhưng bạn đừng phí tiền mua nó, bởi đó là phiên bản cũ từ năm 1994, không hề đề cập tới việc sinh địa hình tự động mà tôi nhắc tới ở đây).
Bẻ cong không gian
Inigo Quiles có đăng một nghiên cứu thú vị về cách dùng fBm để bẻ cong không gian (tức hệ tọa độ) của một fBm khác. Xoắn não thật sự luôn. Chẳng khác gì những giấc mơ lồng nhau trong bộ phim Inception cả.
Một ví dụ dễ thở hơn của kỹ thuật này được thực hiện bởi đoạn code dưới đây, để sinh ra các họa tiết mô phỏng những đám mây. Nếu để ý bạn sẽ đặc tính tương tự nhiều cấp (self-similarity) vẫn có trong họa tiết sau cùng.
<div class='codeAndCanvas' data='clouds.frag'></div>
Việc bẻ công hệ tọa độ của texture bằng nhiễu rất có ích lại còn hay ho nữa, nhưng rất khó để sử dụng thành thạo nếu không kiên trì tập luyện.
Một kỹ thuật khác cũng hay được dùng là dùng nhiễu để can thiệp vào dải gradient khi blend giữa các đỉnh (xét về mặt Toán học, đó chính là đạo hàm của phương trình sóng). Đây là một bài nghiên cứu của Ken Perlin và Fabrice Neyret về "flow noise" dựa trên ý tưởng này. Một vài phiên bản code hiện tại của Perlin noise đã tính cả sóng nhiễu và tích phân của dải màu gradient luôn. Nếu thông tin về dải màu "chi tiết" không được cung cấp thì bạn hoàn toàn có thể dùng phương pháp sai phân hữu hạn để tính xấp xỉ, dù cho kết quả sẽ không được chính xác tuyệt đối và cũng hơi mất công nữa.
---
14/README (14/README.md)
Fractals
Coming soon ...
In the meantime, you can check out some examples with fractals on the shadertoy.com platform:
Basic Fractal
Mandelbrot - distance
Mandelbrot - smooth
Mandelbrot: Power Transitioning
IFS - brute force
Julia - Distance 1
Julia - Distance 3
Julia - Traps 2
Fractal Wheel 2.0
Koch Snowflake again
---
14/README Pt (14/README-pt.md)
Fractais
https://www.shadertoy.com/view/lsX3W4
https://www.shadertoy.com/view/Mss3Wf
https://www.shadertoy.com/view/4df3Rn
https://www.shadertoy.com/view/Mss3R8
https://www.shadertoy.com/view/4dfGRn
https://www.shadertoy.com/view/lss3zs
https://www.shadertoy.com/view/4dXGDX
https://www.shadertoy.com/view/XsXGz2
https://www.shadertoy.com/view/lls3D7
https://www.shadertoy.com/view/XdB3DD
https://www.shadertoy.com/view/XdBSWw
https://www.shadertoy.com/view/llfGD2
https://www.shadertoy.com/view/Mlf3RX
---
14/README Tr (14/README-tr.md)
Fraktallar
Yakında gelecek...
Bu arada, shadertoy.com platformunda fraktallarla ilgili bazı örneklere göz atabilirsiniz:
Temel Fraktal
Mandelbrot - mesafe
Mandelbrot - düzgün
Mandelbrot: Güç Geçişi
IFS - kaba kuvvet
Julia - Mesafe 1
Julia - Mesafe 3
Julia - Tuzaklar 2
Fraktal Çark 2.0
Koch Kar Tanesi tekrar
---
14/README Ua (14/README-ua.md)
Фрактали
Coming soon ...
А поки що можете подивитися приклади з фракталами на платформі shadertoy.com:
Basic Fractal
Mandelbrot - distance
Mandelbrot - smooth
Mandelbrot: Power Transitioning
IFS - brute force
Julia - Distance 1
Julia - Distance 3
Julia - Traps 2
Fractal Wheel 2.0
Koch Snowflake again
---
15/README (15/README.md)
Image processing
Textures
Graphic cards (GPUs) have special memory types for images. Usually on CPUs images are stored as arrays of bytes but GPUs store images as
`sampler2D` which is more like a table (or matrix) of floating point vectors. More interestingly, the values of this table of vectors are continuous. That means that values between pixels are interpolated in a low level.In order to use this feature we first need to upload the image from the CPU to the GPU, to then pass the
`id` of the texture to the right `uniform`. All that happens outside the shader.Once the texture is loaded and linked to a valid
`uniform sampler2D` you can ask for specific color value at specific coordinates (formatted on a `vec2` variable) using the `texture2D()` function which will return a color formatted on a `vec4` variable.glslvec4 texture2D(sampler2D texture, vec2 coordinates)
Check the following code where we load Hokusai's Wave (1830) as
`uniform sampler2D u_tex0` and we call every pixel of it inside the billboard:<div class="codeAndCanvas" data="texture.frag" data-textures="hokusai.jpg"></div>
If you pay attention you will note that the coordinates for the texture are normalized! What a surprise right? Textures coordinates are consistent with the rest of the things we had seen and their coordinates are between 0.0 and 1.0 which match perfectly with the normalized space coordinates we have been using.
Now that you have seen how we correctly load a texture, it is time to experiment to discover what we can do with it, by trying:
* Scaling the previous texture by half.
* Rotating the previous texture 90 degrees.
* Hooking the mouse position to the coordinates to move it.
Why you should be excited about textures? Well first of all forget about the sad 255 values for channel; once your image is transformed into a
`uniform sampler2D` you have all the values between 0.0 and 1.0 (depending on what you set the `precision` to ). That's why shaders can make really beautiful post-processing effects.`vec2()` means you can get values even between pixels. As we said before the textures are a continuum. This means that if you set up your texture correctly you can ask for values all around the surface of your image and the values will smoothly vary from pixel to pixel with no jumps!Finally, you can set up your image to repeat in the edges, so if you give values over or lower of the normalized 0.0 and 1.0, the values will wrap around starting over.
All these features make your images more like an infinite spandex fabric. You can stretch and shrink your texture without noticing the grid of bytes they are originally composed of or the ends of it. To experience this take a look at the following code where we distort a texture using the noise function we already made.
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>
Texture resolution
Above examples play well with squared images, where both sides are equal and match our squared billboard. But for non-squared images things can be a little more tricky, and unfortunately centuries of pictorial art and photography found more pleasant to the eye non-squared proportions for images.
How we can solve this problem? Well we need to know the original proportions of the image to know how to stretch the texture correctly in order to have the original aspect ratio. For that the texture width and height are passed to the shader as an
`uniform`, which in our example framework are passed as an `uniform vec2` with the same name of the texture followed with proposition `Resolution`. Once we have this information on the shader we can get the aspect ratio by dividing the `width` for the `height` of the texture resolution. Finally by multiplying this ratio to the coordinates on `y` we will shrink this axis to match the original proportions.Uncomment line 21 of the following code to see this in action.
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>
* What we need to do to center this image?
Digital upholstery
You may be thinking that this is unnecessarily complicated... and you are probably right. Also this way of working with images leaves enough room to different hacks and creative tricks. Try to imagine that you are an upholster and by stretching and folding a fabric over a structure you can create better and new patterns and techniques.
This level of craftsmanship links back to some of the first optical experiments ever made. For example on games sprite animations are very common, and is inevitably to see on it reminiscence to phenakistoscope, zoetrope and praxinoscope.
This could seem simple but the possibilities of modifying textures coordinates are enormous. For example:
<div class="codeAndCanvas" data="texture-sprite.frag" data-textures="muybridge.jpg"></div>
Now is your turn:
* Can you make a kaleidoscope using what we have learned?
<canvas id="custom" class="canvas" data-fragment-url="texture-kaleidoscope.frag" data-textures="hokusai.jpg" width="300px" height="300px"></canvas>
* Way before Oculus or google cardboard, stereoscopic photography was a big thing. Could you code a simple shader to re-use these beautiful images?
* What other optical toys can you re-create using textures?
In the next chapters we will learn how to do some image processing using shaders. You will note that finally the complexity of shader makes sense, because it was in a big sense designed to do this type of process. We will start doing some image operations!
---
15/NOTES (15/NOTES.md)
No tile images
https://www.shadertoy.com/view/4tsGzf
https://www.shadertoy.com/view/lt2GDd
---
15/README Ch (15/README-ch.md)
图像处理
纹理
显卡(GPU)有特殊的图像存储类型。在中央处理器(CPU)上,图像往往储存成字节数组,但在GPU上图像却往往储存成
`sampler2D` (二维采样器)。它们更像是一个由浮点向量组成的表格(或矩阵)。更有意思的是,这张表格中向量们的值是连续的。这意味着邻近像素点的值之间是用较低级别的插值出来的。为了用到这一特性,我们首先要把图像从CPU上传到GPU,然后再把纹理的
`id`(序列号)传给对应的`uniform`。这一切流程发生在着色器之外。一旦纹理(texture)加载好了并且链接上了一个有效的
`uniform sampler2D`,你就可以用`texture2D()`函数获取特定坐标(用二维向量`vec2`类型表示)对应的颜色值(用四维向量`vec4`类型表示)。glslvec4 texture2D(sampler2D texture, vec2 coordinates)
在下面的代码中,我们将《神奈川冲浪里》(1830)以
`uniform sampler2D u_tex0`为类型和名字加载了进来,并且我们在显示平面(billboard)中调用了它的每个像素:<div class="codeAndCanvas" data="texture.frag" data-textures="hokusai.jpg"></div>
如果你注意观察,你会发现纹理的坐标是归一化的!这真的是个惊喜,不是吗?纹理坐标和我们已然熟识的东西是一致的。它们的坐标总是在0.0和1.0之间。这意味着它和我们使用的归一化空间坐标完美吻合。
现在既然你已经了解了如何正确的加载纹理,是时候来试验性地探索我们究竟能用这一技巧做些什么了。快试试下面这些:
* 把纹理缩小到原先的一半。
* 把原先的纹理旋转90度。
* 将鼠标位置赋值给纹理坐标来移动纹理。
为什么你应该对纹理感到兴奋?首先,忘掉每个通道只有可怜的255个值吧;一旦你的图像被转换成了一个
`uniform sampler2D`,所有的值都会在0.0和1.0的区间范围内(小数精度取决于你设置的`precision`)。这就是为什么着色器可以实现非常漂亮的后期处理效果。`vec2()`类型意味着你甚至可以获取到像素之间的值。如前所述,纹理是连续体(continuum)。也就是说,如果正确地设置好了你的纹理,你可以获取纹理图像上的任意值,而这些值在像素点之间是平滑过渡的,不会出现跳变!最后,你可以把图像设置为在边缘处重复。这样的话,在你获取坐标在0.0到1.0这个归一化区间之外的值时,返回值会从另一边的边缘开始循环往复。
所有这些功能会让你的图像越发像是无限延展的化纤面料。你可以拉扯、伸缩你的纹理而无需注意它们原始的数据究竟是什么样的。要体验这一点,看看下面的代码吧,其中我们使用我们先前创建的噪声函数来扭曲纹理。
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>
纹理分辨率
上述的种种示例仅展现了长宽相等的方形图像匹配方形显示平面(billboard)的情形。而至于非正方形图像,事情就没那么简单了。不幸的是,几个世纪以来的绘画艺术和摄影艺术发现非正方形比例的图像更令人赏心悦目。
我们该如何解决这个问题呢?我们需要知道这一图像的原始比例,才好在放大或缩小纹理的时候正确地保持它原始的宽高比。为此,纹理的宽和高是以
`uniform`的形式传递进着色器的——在我们的示例框架里是以纹理名后跟`Resolution`的`uniform vec2`传递进来的。一旦着色器里有了这些信息,我们就可以将纹理分辨率的`width`除以`height`来获得纹理的宽高比。最后,把这个比例和`y`坐标相乘,我们就可以实现伸缩这根轴来让纹理匹配其原始比例。取消 21 行的注释来实操一下吧。
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>
* 如果要这张图片居中显示,我们应该怎么做呢?
数字装潢(Digital upholstery)
你可能会觉得这未免太复杂了……呃,你大概率是对的。但这种对待图像的方式给了各种“奇技淫巧”足够的发挥空间。试想,你是一名室内装潢师,通过拉伸和折叠布料,你可以创造出更好的新图案和新技巧。
这种水平的工艺可以追溯到最早的一些光学实验。例如,游戏里很常见的精灵动画(sprite animations),你不可避免地会在它身上看到费纳奇镜(phenakistoscope)、走马盘(zoetrope)和旋转活动视镜(praxinoscope)的影子。
这看似简单,但修改纹理坐标可以带来巨大的可能性。例如:
<div class="codeAndCanvas" data="texture-sprite.frag" data-textures="muybridge.jpg"></div>
现在轮到你了:
* 你能活学活用,做出万花筒效果吗?
<canvas id="custom" class="canvas" data-fragment-url="texture-kaleidoscope.frag" data-textures="hokusai.jpg" width="300px" height="300px"></canvas>
* 在Oculus和谷歌Cardboard之前,立体摄影就已经很流行了。你能编写一个简单的着色器来重新使用这些美丽的图像吗?
* 你还可以使用纹理操作来重现哪些其他的光学玩具?
在下一章中,我们将学习如何使用着色器进行一些图像处理。你会注意到着色器的复杂性最终是有意义的,因为它很大程度上是为这一过程而生的。我们将开始做一些图像操作!
---
15/README Pt (15/README-pt.md)
Processamento de imagens
Texturas
Placas gráficas (GPUs) têm tipos especiais de memórias para imagens. Geralmente, nas CPUs as imagens são armazenadas como arrays de bytes, mas as GPUs armazenam as imagens como
`sampler2D`, o que é mais como uma tabela (ou matriz) de vetores de ponto flutuante. Mais interessante ainda, os valores dessa tabela de vetores são contínuos. Isso quer dizer que os valores entre os pixels são interpolados em baixo nível.Para usar essa característica, primeiro precisamos fazer um upload da imagem da CPU para a GPU, para então passar o
`id` da textura para o `uniform` correto. Tudo isso acontece fora do shader.Uma vez que a textura esteja carregada e linkada a um
`uniform sampler2D` válido, você pode solicitar valores específicos de cores em coordenadas específicas (formatadas em uma variiável `vec2`) usanto a função `texture2D()` que vai retornar uma cor formatada em uma variável `vec4`.glslvec4 texture2D(sampler2D texture, vec2 coordinates)
Verifique o seguinte código onde carregamos a Onda de Hokusai (1830) como um
`uniform sampler2D u_tex0` e chamamos cada pixel dela dentro da tela:<div class="codeAndCanvas" data="texture.frag" data-textures="hokusai.jpg"></div>
Se você prestar atenção, vai notar que as coordenadas da textura estão normalizadas! Que surpresa, certo? As coordenadas das texturas são consistentes com o resto das coisas que já vimos e suas coordenadas estão entre 0.0 e 1.0, o que combina perfeitamente com o espaço normalizado de coordenadas que temos usado.
Agora que você já viu como carregamos uma textura de forma correta, é hora de fazer experiências, para descobrir o que podemos fazer com isso, tentando:
* Escalar a textura anterior, pela metade.
* Rotacionar a textura anterior em 90 graus.
* Ligar a posição do mouse às coordenadas, e movê-la.
Por que você deveria estar anomado com as texturas? Bem, antes de tudo, esqueça os 255 valores tristes para o canal, uma vez que sua imagem esteja transformada em um
`uniform sampler2D` você terá todos os valores entre 0.0 e 1.0 (dependendo do que você setar na `precision`). É por isso que as os shaders podem fazer efeitos realmente lindos pós-processament.`vec2()` significa que você pode pegar os valores entre os pixels. Como dizemos antes, as texturas são um continuum. Isso quer dizer que se você configurar sua textura corretamente, você pode pedir valores em torno de toda a superfície de sua imagem, e os valores vão variar suavemente de pixel a pixel, sem pulos!Por fim, você pode setar sua imagem para repetir nas bordas, então você pode dar valores além ou menores que os normalizados 0.0 e 1.0, os valores vão dar a volta e recomeçar.
Todas essas características fazem suas imagens mais como uma fábrica infinita. Você pode esticar e encolher sua textura sem notar a grade de bytes de que elas são compostas originalmente, ou o fim dela. Para experimentar isso, dê uma olhada no seguinte código, onde nós distorcemos uma textura usando a função de ruído que já fizemos.
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>
Resolução de Texturas
Os exemplos acima rodam bem com imagens quadradas, onde os dois lados são iguais e combinam uma tela quadrada. Mas para imagens não-quadradas, as coisas podem ser um pouco mais complicadas, e infelizmente, séculos de art de imagens e fotos descobriram ser mais agradáveis aos olhos as proporções não-quadradas.
Como podemos resolver este problema? Bem, precisamos saber as proporções originais da imagem para saber como esticar a textura corretamente, de forma a ter o aspect ratio (proporção da tela) original. Para isso, a largura e altura da textura são passados para o shader como um
`uniform` que, no nosso framework de exemplo são passados como um `uniform vec2` com o mesmo nome da textura seguida da proposição `Resolution`. Uma vez que temos essa informação no shader, podemos pegar o aspect ratio dividindo-se o `width` (largura) pelo `height` (altura) da resolução da textura. Finalmente, multiplicando-se essa taxa com as coordenadas em `y` vamos encolher esse eixo para combinar com as proporções originais.Descomente a linha 21 do código a seguir para ver isso em ação.
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>
* O que precisamos fazer para centralizar essa imagem?
Estofamento digital
Você deve estar pensando que isso é algo desnecessariamente complicado... e provavelmente está certo. Também, esse modo de trabalhar com imagens deixa espaço suficiente para hacks diferentes e truques criativos. Tente imaginar que você é um estofador e, ao esticar e dobrar um tecido sobre uma estrutura, pode criar padrões e técnicas novas e melhores.
Esse nível de artesanato faz referência a alguns dos primeiros experimentos óticos já feitos. Por exemplo, em animações de sprites de jogos, são bem comuns, e é inevitável ver nele uma reminiscência ao fenacistoscópio, ao zootropo e ao praxinoscópio.
Isso poderia parecer simples, mas as possibilidades de modificar coordenadas de texturas são enormes. Por exemplo:
<div class="codeAndCanvas" data="texture-sprite.frag" data-textures="muybridge.jpg"></div>
Agora é sua vez:
* Você consegue fazer um caleidoscópio usando o que aprendemos?
* Muito antes do Oculus ou do cardboard do google, a fotografoa estereoscópica era uma coisa incrível. Você poderia programar um shader simples para reutilizar essas lindas imagens?
<a href=“../edit.php#10/ikeda-03.frag”><canvas id=“custom” class=“canvas” data-fragment-url=“ikeda-03.frag” width=“520px” height=“200px”></canvas></a>
* Que outros brinquedos óticos você pode recriar usando texturas?
Nos próximos capítulos, você vai aprender como fazer algum processamento em imagens, com os shaders. Vai notar que, finalmente, a complexidade do shaders faz sentido, porque ele foi, de certa forma, projetada para fazer esse tipo de processo. Vamos começar a fazer alguma operação com imagens!
---
15/README Tr (15/README-tr.md)
Görüntü işleme
Dokular
Grafik kartları (GPU'lar), görüntüler için özel bellek türlerine sahiptir. Genellikle CPU'larda görüntüler bayt dizileri olarak depolanır, ancak GPU'lar görüntüleri kayan nokta vektörlerinin tablosu (veya matrisi) gibi olan
`sampler2D` olarak depolar. Daha ilginç olarak, bu vektörler tablosunun değerleri süreklidir. Bu, pikseller arasındaki değerlerin düşük seviyede enterpolasyon yapıldığı anlamına gelir.Bu özelliği kullanmak için önce görüntüyü CPU'dan GPU'ya yüklememiz, ardından dokunun
`id`'sini doğru `uniform`'a geçirmemiz gerekir. Tüm bunlar shader'ın dışında gerçekleşir.Doku yüklenip geçerli bir
`uniform sampler2D`'ye bağlandıktan sonra, `texture2D()` fonksiyonunu kullanarak belirli koordinatlarda (`vec2` değişkeni olarak formatlanmış) belirli renk değerini isteyebilirsiniz, bu da `vec4` değişkeni olarak formatlanmış bir renk döndürecektir.glslvec4 texture2D(sampler2D texture, vec2 coordinates)
Hokusai'nin Dalgası'nı (1830)
`uniform sampler2D u_tex0` olarak yüklediğimiz ve tuvalin içindeki her pikselini çağırdığımız aşağıdaki kodu kontrol edin:<div class="codeAndCanvas" data="texture.frag" data-textures="hokusai.jpg"></div>
Dikkat ederseniz, doku koordinatlarının normalize edildiğini fark edeceksiniz! Ne sürpriz, değil mi? Doku koordinatları gördüğümüz diğer şeylerle tutarlıdır ve koordinatları, kullandığımız normalize edilmiş uzay koordinatlarıyla mükemmel şekilde eşleşen 0.0 ile 1.0 arasındadır.
Artık bir dokuyu doğru şekilde nasıl yükleyeceğimizi gördüğünüze göre, onunla ne yapabileceğimizi keşfetmek için deney yapma zamanı:
* Önceki dokuyu yarıya ölçekleyin.
* Önceki dokuyu 90 derece döndürün.
* Fare konumunu koordinatlara bağlayarak hareket ettirin.
Dokular hakkında neden heyecanlanmalısınız? Her şeyden önce kanal başına üzücü 255 değeri unutun; görüntünüz
`uniform sampler2D`'ye dönüştürüldüğünde 0.0 ile 1.0 arasındaki tüm değerlere sahip olursunuz (`precision`'a ne ayarladığınıza bağlı olarak). Bu yüzden shader'lar gerçekten güzel son işleme efektleri yapabilir.`vec2()`, pikseller arasında bile değerler elde edebileceğiniz anlamına gelir. Daha önce söylediğimiz gibi dokular bir sürekliliktir. Bu, dokunuzu doğru ayarlarsanız, görüntünüzün yüzeyi boyunca değerler isteyebileceğiniz ve değerlerin pikselden piksele atlamalar olmadan yumuşak bir şekilde değişeceği anlamına gelir!Son olarak, görüntünüzü kenarlarda tekrarlanacak şekilde ayarlayabilirsiniz, bu nedenle normalize edilmiş 0.0 ve 1.0'ın üstünde veya altında değerler verirseniz, değerler baştan sarılacaktır.
Tüm bu özellikler görüntülerinizi daha çok sonsuz bir kumaş gibi yapar. Dokunuzu, orijinal olarak oluşturuldukları bayt ızgarasını veya sonlarını fark etmeden esnetebilir ve küçültebilirsiniz. Bunu deneyimlemek için zaten yaptığımız gürültü fonksiyonunu kullanarak bir dokuyu bozdurduğumuz aşağıdaki koda bakın.
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>
Doku çözünürlüğü
Yukarıdaki örnekler her iki tarafın eşit olduğu ve kare tuvalimizle eşleştiği kare görüntülerle iyi çalışır. Ancak kare olmayan görüntüler için işler biraz daha karmaşık olabilir ve ne yazık ki yüzyıllarca süren resim sanatı ve fotoğrafçılık, göze kare olmayan oranların daha hoş geldiğini keşfetmiştir.
Bu sorunu nasıl çözebiliriz? Dokuyu orijinal en-boy oranına sahip olacak şekilde doğru germek için görüntünün orijinal oranlarını bilmemiz gerekir. Bunun için dokunun genişliği ve yüksekliği shader'a
`uniform` olarak geçirilir, örnek framework'ümüzde dokunun adıyla aynı adlı `uniform vec2`'ler `Resolution` son ekiyle geçirilir. Bu bilgiyi shader'da elde ettikten sonra, doku çözünürlüğünün `genişliğini` `yüksekliğine` bölerek en-boy oranını elde edebiliriz. Son olarak, bu oranı `y`'deki koordinatlarla çarparak bu ekseni orijinal oranlara uyacak şekilde küçülteceğiz.Bunu uygulamada görmek için aşağıdaki kodun 21. satırının yorumunu kaldırın.
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>
* Bu görüntüyü ortalamak için ne yapmamız gerekir?
Dijital döşeme
Bunun gereksiz yere karmaşık olduğunu düşünüyor olabilirsiniz... ve muhtemelen haklısınız. Ayrıca görüntülerle çalışmanın bu yolu, farklı hileler ve yaratıcı numaralar için yeterli alan bırakır. Bir döşemeci olduğunuzu ve bir yapı üzerinde kumaşı gererek ve katlayarak daha iyi ve yeni desenler ve teknikler yaratabileceğinizi hayal edin.
Bu zanaatkarlık seviyesi, şimdiye kadar yapılmış ilk optik deneylerin bazılarına bağlıdır. Örneğin oyunlarda sprite animasyonları çok yaygındır ve fenakistoskop, zeotrop ve praksiskop anımsatmalarını görmek kaçınılmazdır.
Bu basit görünebilir ama doku koordinatlarını değiştirme olanakları muazzamdır. Örneğin:
<div class="codeAndCanvas" data="texture-sprite.frag" data-textures="muybridge.jpg"></div>
Şimdi sıra sizde:
* Öğrendiklerimizle bir kaleydoskop yapabilir misiniz?
<canvas id="custom" class="canvas" data-fragment-url="texture-kaleidoscope.frag" data-textures="hokusai.jpg" width="300px" height="300px"></canvas>
* Oculus veya Google Cardboard'dan çok önce, stereoskopik fotoğrafçılık büyük bir şeydi. Bu güzel görüntüleri yeniden kullanmak için basit bir shader kodlayabilir misiniz?
* Dokular kullanarak başka hangi optik oyuncakları yeniden yaratabilirsiniz?
Sonraki bölümlerde shader'lar kullanarak bazı görüntü işleme yapmayı öğreneceğiz. Shader'ın karmaşıklığının nihayet mantıklı olduğunu göreceksiniz, çünkü geniş anlamda bu tür işlemleri yapmak için tasarlanmıştır. Bazı görüntü operasyonları yapmaya başlayacağız!
---
15/README Ua (15/README-ua.md)
Обробка зображень
Текстури
Графічні карти (GPU) мають для зображень спеціальні типи пам’яті. Зазвичай CPU зберігають зображення у вигляді масиву байтів, а GPU зберігають зображення як
`sampler2D`, що більше схоже на таблицю (або матрицю) векторів типу float. Що ще цікавіше, значення цієї таблиці векторів безперервні. Це означає, що значення між пікселями інтерполюються на низькому рівні.Щоб скористатися цим функціоналом, нам спочатку потрібно завантажити зображення з CPU на GPU, а потім передати
`id` текстури до відповідної `uniform`-змінної. Початкове завантаження зображення відбувається поза шейдером.Після того, як текстуру завантажено та прив'язано до змінної через
`uniform sampler2D`, ви можете отримати конкретне значення кольору у певних координатах типу `vec2` за допомогою функції `texture2D()` яка поверне колір типу `vec4`.glslvec4 texture2D(sampler2D texture, vec2 coordinates)
Перегляньте наступний код, у якому ми завантажуємо зображення Хвилі Хокусая (1830) як
`uniform sampler2D u_tex0`, отримуємо та відображаємо кожен її піксель всередині полотна:<div class="codeAndCanvas" data="texture.frag" data-textures="hokusai.jpg"></div>
Якщо ви звернете увагу, то помітите, що координати для текстури нормалізовані! Який сюрприз, правда? Координати текстур консистентні з нормалізованими координатами простору, що також знаходяться в діапазоні від 0.0 до 1.0.
Тепер, коли ви побачили, як правильно завантажувати текстуру, настав час поекспериментувати та дізнатися, що можна з нею зробити:
* Змініть масштаб текстури вдвічі.
* Поверніть текстуру на 90 градусів.
* Прив'яжіть координати до позиції курсору, щоб переміщати зображення.
Чому ви повинні бути в захваті від текстур? По-перше, забудьте про сумні 255 значень для кожного каналу. Як тільки ваше зображення перетворено на
`uniform sampler2D`, ви працюватимете у діапазоні значень від 0.0 до 1.0. Ось чому шейдери можуть створювати дійсно красиві ефекти постобробки.`vec2` ви можете отримати значення кольору текстури навіть між пікселями. Як ми вже говорили раніше, значення текстури є континуумом. Це означає, що ви можете отримувати значення вашого зображення по всій його поверхні. Ці значення плавно змінюватимуться від пікселя до пікселя без стрибків!Нарешті, ви можете налаштувати шейдер для повторення вашого зображення вздовж полотна. Наприклад, при використанні значень, що менше або більше від нормалізованих 0.0 та 1.0 ви можете "загорнути" їх у цей самий діапазон та знову отримати кольори в межах зображення.
Усі ці можливості роблять ваші зображення схожими на нескінченну тканину спандекс. Ви можете розтягувати та звужувати свою текстуру, не зважаючи на сітку байтів, з яких вона складалася початково. Щоб відчути це, подивіться на наступний код, де ми деформуємо текстуру за допомогою функції шуму, з попередніх розділів.
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>
Роздільна здатність текстури
Наведені вище приклади добре працюють з квадратними зображеннями, де обидві сторони рівні та збігаються з нашим квадратним полотном. Але для неквадратних зображень все може бути трохи складніше. Століття художнього мистецтва та фотографії знайшли для зображень більш приємні для ока неквадратні пропорції.
Як ми можемо розв'язати цю проблему? Щоб дізнатися як правильно розтягнути текстуру, нам потрібно знати оригінальні пропорції зображення, що дозволить отримати його співвідношення сторін. Для цього ширина та висота текстури також передаються шейдеру через
`uniform`, який у нашому прикладі буде мати тип `vec2`. Ми надали цій змінній таку саму назву як і для текстури, але з доповненням `Resolution`. Отримавши цю інформацію, ми можемо отримати у шейдері співвідношення сторін, поділивши `ширину` на `висоту`. Нарешті, помноживши це співвідношення на координату `y`, ми змінимо цю вісь таким чином, щоб вона відповідала бажаним пропорціям.Розкоментуйте рядок 21 наступного коду, щоб побачити це в дії:
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>
* Поміркуйте, що потрібно зробити, щоб відцентрувати це зображення по центру полотна?
Цифрова оббивка
Ви можете подумати, що все це трохи ускладнено... й, напевно, матимете рацію. Але такий спосіб роботи з зображеннями залишає достатньо місця для різних хитрощів та творчих прийомів. Спробуйте уявити, що ви робите оббивку, а натягуючи та огортаючи тканину поверх конструкції, ви можете створювати нові патерни та цікавіші результати.
Подібний рівень ремісництва нагадує деякі з перших оптичних експериментів. Наприклад, в іграх дуже поширена спрайтова анімація, що навіває спогади про фенакістископ, зоотроп та праксиноскоп.
Виглядає просто, але можливості для зміни текстурних координат просто величезні.
<div class="codeAndCanvas" data="texture-sprite.frag" data-textures="muybridge.jpg"></div>
Тепер ваша черга:
* Спробуйте зробити калейдоскоп:
<canvas id="custom" class="canvas" data-fragment-url="texture-kaleidoscope.frag" data-textures="hokusai.jpg" width="300px" height="300px"></canvas>
* Задовго до Oculus та google cardboard стереоскопічна фотографія була неабиякою справою. Чи можете ви закодувати простий шейдер, щоб повторно використати ці чудові зображення?
* Які ще оптичні іграшки можна відтворити за допомогою текстур?
У наступних розділах ми побачимо як за допомогою шейдерів можна обробляти зображення, виконуючи певні операції. Зрештою шейдери не в останню чергу розроблені саме для цього.
---
16/README (16/README.md)
Image operations
Below you will see some examples with images where you can play around and uncomment some lines to see the corresponding results.
Invert
<div class="codeAndCanvas" data="inv.frag" data-textures="00.jpg,01.jpg"></div>
Add, Subtract, Multiply and others
<div class="codeAndCanvas" data="operations.frag" data-textures="00.jpg,01.jpg"></div>
PS Blending modes
<div class="codeAndCanvas" data="blend.frag" data-textures="04.jpg,05.jpg"></div>
---
16/README Tr (16/README-tr.md)
Görüntü işlemleri
Aşağıda görüntülerle oynayabileceğiniz ve karşılık gelen sonuçları görmek için bazı satırları yorumdan çıkarabileceğiniz örnekleri göreceksiniz.
Tersine çevirme
<div class="codeAndCanvas" data="inv.frag" data-textures="00.jpg,01.jpg"></div>
Toplama, Çıkarma, Çarpma ve diğerleri
<div class="codeAndCanvas" data="operations.frag" data-textures="00.jpg,01.jpg"></div>
PS Harmanlama modları
<div class="codeAndCanvas" data="blend.frag" data-textures="04.jpg,05.jpg"></div>
---
16/README Ua (16/README-ua.md)
Операції із зображеннями
Нижче ви побачите кілька прикладів із зображеннями, де ви можете пограти зі значеннями та розкоментувати певні рядки, щоб побачити відповідний результат.
Інвертування
Розкоментуйте рядок 18:
<div class="codeAndCanvas" data="inv.frag" data-textures="00.jpg,01.jpg"></div>
Додавання, віднімання, множення та інші варіанти
В даному шейдері завантажено два зображення, які розташовано один над одним. По черзі розкоментовуйте рядки 22-27 та спостерігайте за результатами поєднання цих зображень залежно від операції. Перш ніж розкоментувати рядок, поміркуйте над тим який має бути результат і чому:
<div class="codeAndCanvas" data="operations.frag" data-textures="00.jpg,01.jpg"></div>
Режими змішування як у Photoshop
<div class="codeAndCanvas" data="blend.frag" data-textures="04.jpg,05.jpg"></div>
---
Appendix/README (appendix/README.md)
Appendix
1. How can I navigate this book off-line?
2. How to run the examples on a Raspberry Pi?
4. How can I collaborate with this book?
5. An introduction for those coming from JS by Nicolas Barradeau
6. An introduction for vectors by ...
7. An introduction to interpolation by ...
---
Appendix/README Ch (appendix/README-ch.md)
附录
1. 如何离线阅读此书?
3. 如何打印这本书?
4. 我怎样共创这本书?
5. 给那些从JS语言过来的人的介绍 Nicolas Barradeau著
6. 向量导论 by ...
7. 插值导论 by ...
---
Appendix/README De (appendix/README-de.md)
Appendix
1. Wie kann ich dieses Buch offline lesen?
2. Wie kann ich die Beispielprogramme auf einem Raspberry Pi ausführen?
3. Wie kann ich dieses Buch ausdrucken?
4. Wie kann ich zu diesem Buch beitragen?
5. An introduction for those coming from JS by Nicolas Barradeau
6. An introduction for vectors by ...
7. An introduction to interpolation by ...
---
Appendix/README Fr (appendix/README-fr.md)
Annexes
1. Comment puis-je consulter ce livre hors-ligne ?
2. Comment exécuter les exemples sur un Raspberry Pi ?
3. Comment imprimer ce livre ?
4. Comment puis-je contribuer à ce livre?
5. Une introduction pour ceux qui viennent de JS de Nicolas Barradeau
6. Une introduction aux vecteurs de ...
7. Une introduction à l'interpolation de ...
---
Appendix/README Id (appendix/README-id.md)
Lampiran
1. Bagaimana saya mendapatkan buku ini secara offline?
2. Bagaimana cara menjalankan contoh di Raspberry Pi?
3. Bagaimana mencetak buku ini?
4. Bagaimana saya bisa berkolaburasi?
5. An introduction for those coming from JS by Nicolas Barradeau
6. An introduction for vectors by ...
7. An introduction to interpolation by ...
---
Appendix/README Pl (appendix/README-pl.md)
Dodatek
1. Jak mogę nawigować po tej książce w trybie off-line?
2. Jak uruchomić przykłady na Raspberry Pi?
4. Jak współpracować przy tworzeniu tej książki?
5. Wprowadzenie dla osób pochodzących z JS autorstwa Nicolas Barradeau
6. Wprowadzenie dla wektorów autorstwa ...
7. Wprowadzenie do interpolacji przez ...
---
Appendix/README Ru (appendix/README-ru.md)
Приложение
2. Как запустить примеры на Raspberry Pi?
4. Как принять участие в создании книги?
5. Введение для JavaScript-программистов (Николя Баррадо)
6. Введение в векторную алгебру
---
Appendix/README Tr (appendix/README-tr.md)
Ek
1. Bu kitapta çevrimdışı nasıl gezinirim?
2. Örnekleri Raspberry Pi'da nasıl çalıştırırım?
3. Bu kitabı nasıl yazdırabilirim?
4. Bu kitaba nasıl katkıda bulunabilirim?
5. JS'den gelenler için giriş - Nicolas Barradeau
6. Vektörlere giriş - ...
7. Enterpolasyona giriş - ...
---
Appendix/README Ua (appendix/README-ua.md)
Додаток
1. Як користуватись книгою офлайн?
2. Як запустити приклади на Raspberry Pi?
4. Як прийняти участь у розвитку книги?
5. Вступ для тих, хто прийшов із JS від Nicolas Barradeau
---
Appendix/README Vi (appendix/README-vi.md)
Phụ lục
1. Làm thế nào để đọc quyển sách này mà không cần Internet ?
2. Làm thế nào để chạy thử ví dụ trên Raspberry Pi ?
3. Làm thế nào để in quyển sách này ?
4. Làm thế nào để góp sức cho quyển sách này ?
5. Giới thiệu cho người đã biết Javascript từ Nicolas Barradeau
6. Giới thiệu về vector từ ...
7. Giới thiệu về nội suy từ ...
---
Appendix/00/README (appendix/00/README.md)
How can I navigate this book off-line?
Let’s say you have a long trip and you want to use it to teach yourself some shaders. In that case you can make a local copy of this book on your computer and run a local server.
For that you only need PHP, Python 3 and a git client. On MacOS and Raspberry Pi computers Python is installed by default but you still need to install PHP and a git client. For that:
On MacOSX be sure to have homebrew installed and then on your terminal do:
bashbrew update
brew upgrade
brew install git php
On Raspberry Pi you need to get Raspbian, a Debian-based Linux distribution made for Raspberry Pi and then do:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Once you have everything installed you just need to do:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
http://localhost:8000/---
Appendix/00/README De (appendix/00/README-de.md)
Wie kann ich dieses Buch offline lesen?
Nehmen wir einmal an, Du gehst auf eine längere Reise und möchtest Dich unterwegs in die Shader-Programmierung einarbeiten. In diesem Fall könntest Du eine Kopie dieses Buches auf Deinen Rechner laden und diese über einen lokalen Server anzeigen lassen.
Alles, was Du dafür benötigst, ist Python 3 und ein Client-Programm für GIT. Auf Computern mit MacOS und auf dem RaspberryPi ist Python standardmäßig bereits vorinstalliert. Dort musst Du lediglich den GIT-Client zusätzlich installieren.
Unter MacOSX benötigst Du dafür homebrew. Sobald das Paket installiert ist, kannst Du in einem Terminal-Fenster folgendes eingeben:
bashbrew update
brew upgrade
brew install git
Auf einem RaspberryPi gibst Du folgendes ein:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
Hast Du alles beisammen, musst Du lediglich folgende Befehle aufrufen, um das Book of Shaders per GIT auf Deinen Rechner zu laden und den Server zur Anzeige des Inhalts zu starten:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
Nutze dann den vorinstallierten Internet-Browser auf Deinem Rechner zur Anzeige des Buches durch Eingabe folgender URL
`http://localhost:8000/`---
Appendix/00/README Fr (appendix/00/README-fr.md)
Comment puis-je consulter ce livre hors-ligne ?
Disons que vous vous préparez à partir pour un long voyage et que vous souhaitez le mettre à profit pour apprendre quelques shaders. Dans ce cas, pourquoi pas créer une copie locale de ce livre sur votre ordinateur et lancer un serveur local.
Pour cela, vous n'avez besoin que de PHP, Python 3 et d'un client git. Sur les ordinateurs MacOS et Raspberry Pi, Python est installé par défaut, mais vous il vous reste à installer PHP et un client git. Pour cela :
Sous MacOSX, assurez-vous d'avoir homebrew installé et ensuite dans votre terminal :
bashbrew update
brew upgrade
brew install git php
Sur votre Raspberry Pi, vous devez installer Raspbian, une distribution Linux basée sur Debian conçu pour Raspberry Pi, puis lancer :
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Une fois que vous avez tout installé, il vous suffit d'exécuter les commandes :
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
http://localhost:8000/, et voilà !---
Appendix/00/README Id (appendix/00/README-id.md)
Bagaimana saya mendapatkan buku ini secara offline?
Katakanlah anda sedang berada di perjalanan jauh dan anda ingin mempelajari beberapa shader, Dalam kasus itu anda dapat membuat salinan lokal dari buku ini di komputermu dan berjalan di server lokal.
Untuk itu anda membutuhkan PHP, Python 3 dan Git Client. Pada komputer MacOS dan Raspberry Pi, Python sudah terpasang secara bawaan tapi anda tetap harus menginstall git client:
Pada MacOSX, pastikan mempunyai homebrew yang terpasang dan terminal lakukanlah:
bashbrew update
brew upgrade
brew install git php
Pada Raspberry Pi, anda harus mempunyai Raspbian, sebuah distribusi Linux berbasis Debian yang dibuat untuk Raspberry Pi dan lakukanlah:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Setelah semua terpasang, anda hanya perlu melakukan:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
Lalu di browser anda kunjungi 'http://localhost:8000/'
---
Appendix/00/README It (appendix/00/README-it.md)
Come posso consultare questo libro offline?
Diciamo che si dispone di un lungo viaggio e si desidera usarlo per imparare da soli alcuni shaders. In questo caso è possibile effettuare una copia locale di questo libro sul computer ed eseguire un server locale.
Per questo è necessario solo Python 3 e un client git. Sui computer MacOS e Raspberry Pi, Python è installato di default, ma è comunque necessario installare un client git. Per fare ciò:
Su MacOSX siate sicuri di avere installato homebrew e quindi sul terminale fate:
bashbrew update
brew upgrade
brew install git
Su Raspberry Pi è necessario fare:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
Una volta che avete installato tutto, non vi resta che fare:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
`http://localhost:8000/`---
Appendix/00/README Pl (appendix/00/README-pl.md)
Jak mogę korzystać z tej książki offline?
Powiedzmy, że masz przed sobą długą podróż i chcesz ją wykorzystać do nauczenia się kilku shaderów. W takim przypadku możesz zrobić lokalną kopię tej książki na swoim komputerze i uruchomić lokalny serwer.
Do tego potrzebujesz tylko PHP, Pythona 3 i Git'a. Na komputerach z systemem macOS i Raspberry Pi Python jest zainstalowany domyślnie, ale musisz jeszcze zainstalować PHP i klienta Git. W tym celu:
Na MacOSX musisz mieć zainstalowane homebrew by następnie w terminalu uruchomić:
bashbrew update
brew upgrade
brew install git php
Na Raspberry Pi potrzebujesz Raspbian, opartej na Debianie dystrybucji Linuxa, by następnie uruchomić:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Gdy masz już wszystko zainstalowane wystarczy uruchomić:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
http://localhost:8000/---
Appendix/00/README Ru (appendix/00/README-ru.md)
Как читать книгу оффлайн?
Предположим, вы отправились в длительное путешествие, и хотите немного подучить программирование шейдеров за это время. В этом случае вы можете скопировать книгу к себе на компьютер и запустить сервер локально.
Для этого понадобится только PHP, Python 3 и git-клиент. На MacOS и Raspberry Pi Python установлен по умолчанию, но PHP и Git придётся установить следующим образом:
На MacOSX убедитесь, что у вас установлен homebrew и выполните следующие команды в терминале:
bashbrew update
brew upgrade
brew install git php
На Raspberry Pi установите Raspbian - дистрибутив Linux для Raspberry Pi, основанный на Debian, и выполните:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Когда всё установлено, остаётся последний шаг:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
http://localhost:8000/---
Appendix/00/README Tr (appendix/00/README-tr.md)
Bu kitapta çevrimdışı nasıl gezinirim?
Diyelim ki uzun bir yolculuğunuz var ve bunu kendinize shader öğretmek için kullanmak istiyorsunuz. Bu durumda bu kitabın yerel bir kopyasını bilgisayarınıza yapabilir ve yerel bir sunucu çalıştırabilirsiniz.
Bunun için yalnızca PHP, Python 3 ve bir git istemcisine ihtiyacınız var. MacOS ve Raspberry Pi bilgisayarlarında Python varsayılan olarak yüklüdür ancak yine de PHP ve git istemcisi yüklemeniz gerekir. Bunun için:
MacOSX'te homebrew yüklü olduğundan emin olun ve ardından terminalinizde şunları yapın:
bashbrew update
brew upgrade
brew install git php
Raspberry Pi'da, Raspberry Pi için yapılmış Debian tabanlı bir Linux dağıtımı olan Raspbian'ı edinmeniz ve ardından şunları yapmanız gerekir:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Her şey yüklendikten sonra yapmanız gereken tek şey:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
http://localhost:8000/ adresine açın---
Appendix/00/README Ua (appendix/00/README-ua.md)
Як користуватись книгою офлайн?
Припустимо, у вас довга подорож і ви хочете за цей час повчити шейдери. У такому випадку ви можете скопіювати книгу на свій комп'ютер і запустити її на локальному сервері.
Для цього вам потрібні лише PHP, Python 3 і git-клієнт. На комп'ютерах MacOS і Raspberry Pi Python встановлено за замовчуванням, але потрібно ще встановити PHP і git-клієнт. Це можна зробити наступним чином:
Для MacOSX переконайтесь, що у вас встановлено homebrew, після чого в терміналі виконайте наступні команди:
bashbrew update
brew upgrade
brew install git php
На Raspberry Pi встановіть Raspbian, дистрибутив Linux для Raspberry Pi на основі Debian, а потім виконайте наступні команди:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Після того, як все буде встановлено, склонуйте проєкт та запустіть локальний сервер:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
http://localhost:8000/---
Appendix/00/README Vi (appendix/00/README-vi.md)
Làm thế nào để đọc quyển sách này mà không cần Internet ?
Giả sử bạn có một chuyến đi dài và muốn tranh thủ học shader. Trong trường hợp đó bạn có thể tạo một bản sao của quyển sách này và hiển thị nó bằng server nội bộ.
Để làm điều đó, bạn chỉ cần có PHP, Python 2.6 và git. Trên các máy MacOS và Raspberry Pi thì Python đã được cài đặt sẵn rồi nhưng bạn vẫn cần cài thêm PHP và git. Cụ thể:
Trên MacOSX hãy cài homebrew trước rồi mở terminal ra và gõ:
bashbrew update
brew upgrade
brew install git php
Trên Raspberry Pi bạn cần cài Raspbian, là một phiên bản Linux dựa trên Debian dành riêng cho Raspberry Pi, sau đó gõ lệnh:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
Khi đã cài đặt xong các phần mềm cần thiết, bạn chỉ cần gõ:
bashcd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
http://localhost:8000/---
Appendix/01/README (appendix/01/README.md)
How to run the examples on a Raspberry Pi?
A few years ago, assuming that everybody has a computer with a graphical processing unit was a long shot. Now, most computers have a GPU, but it's still a high bar for a requirement in a workshop or class, for example.
Thanks to the Raspberry Pi Foundation a new type of small and cheap generation of computers (around $35 each) has found its way into classrooms. More importantly for the purposes of this book, the Raspberry Pi comes with a decent Broadcom GPU that can be accessed directly from the console. I made a flexible GLSL live coding tool call glslViewer that runs all the examples in this book. This program also has the ability to update automatically when the user saves a change to their code. What does this mean? You can edit the shader and every time you save it, the shader will be re-compile and render for you.
By making a local copy of the repository of this book (see the above section) and having
glslViewer installed, users can run the examples with glslviewer. Also by using the -l flag they can render the example in a corner of the screen while they modify it with any text editor (like nano, pico, vi, vim or emacs). This also works if the user is connected through ssh/sftp.To install and set this all up on the Raspberry Pi after installing Raspbian, a Debian-based Linux distribution made for Raspberry Pi, and logging in, type the following commands:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README Ch (appendix/01/README-ch.md)
如何在树莓派上运行示例程序?
几年前,假设每个人都有一台有图形处理单元的计算机是一个大胆的想法。 现在,大多数计算机都有 GPU,但对于工作坊或课堂的要求来说,这仍然是个很高的标准。
多亏了 树莓派基金会, 一种新型的小型廉价计算机(每台约35美元)已被引入课堂。更重要的是,就本书的需求而言,树莓派配备了一个不错的可以直接从控制台访问的Broadcom GPU。我做了一个 叫作glslViewer的灵活的GLSL实时编程工具,它可以运行这本书里全部的示例。当用户保存对其代码的更改时,该编程工具还具有自动更新的能力。这是啥意思? 就是说,你可以编辑着色器,每次保存时,着色器都会重新编译并渲染。
通过制作本书的本地副本(详情见上一章节) 并 安装好
glslViewer,用户可以使用glslviewer运行示例。此外,通过使用 -l 标志,它们可以在你用任何文本编辑器(例如 nano, pico, vi, vim 或 emacs)来修改代码的时候在屏幕角落渲染示例。如果用户通过ssh或sftp协议连接,这也有效。在树莓派上装完 Raspbian——一个为树莓派开发的基于Debian的Linux发行版本——之后,要在树莓派上装好搞好这一切,只需要登陆系统,输入如下指令:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README De (appendix/01/README-de.md)
Wie kann ich die Beispiele auf einem RaspberryPi ausführen?
Vor wenigen Jahren konnte man noch nicht davon ausgehen, dass jedermann über einen Computer mit einer GPU verfügt. Heutzutage gilt das nicht mehr. Doch im Bereich von Schulen, Universitäten und anderen Weiterbildungseichrichtungen ist dies immer noch eine hohe Anforderung.
Durch das Raspberry Projekt hat mittlerweile eine neue Generation kleiner und billiger Computer (das Stück ab ca. 40 Euro) ihren Weg in viele Klassenzimmer gefunden. Vor allem verfügt der RaspberryPi über einen ansehnlichen Grafikchip mit GPU, der direkt aus der Kommandozeile angesprochen werden kann.
Ich habe ein flexibles Werkzeug für die Programmierung von GLSL-Shadern entwickelt, den glslViewer. Damit können alle Beispiele aus diesem Buch ausgeführt werden. Sobald man Veränderungen am aktuellen Shader-Programmcode abspeichert, kompiliert das Programm den Shader erneut und bringt ihn zur Ausführung. Auf dem Bildschirm erscheint daraufhin die aktualisierte Anzeige des Shaders.
Indem Du eine lokale Kopie dieses Buches und seiner Beispielprogramme auf Deinem Rechner anlegst (mehr dazu im vorhergehenden Kapitel) und den [
`glslViewer`] (https://github.com/patriciogonzalezvivo/glslViewer) installierst, kannst Du die Beispielprogramme mit dem `glslviewer` ausführen. Wenn Du beim Start dieses Programms den Kommandozeilenschalter `-l` angibst, erscheint die erzeugte Grafik in einer Ecke des Bildschirms, während Du gleichzeitig den Shader-Programmcode mit einem beliebigen Text-Editor (etwa `nano`, `pico`, `vi`, `vim` oder `emacs`) bearbeitest. Das funktioniert auch, wenn Du mit dem Terminal des RaspberryPi über ssh/sftp verbunden bist.Um die angesprochenen Tools auf einem RaspberryPi zu installieren, rufe nach dem Start des Betriebssystems und dem Einloggen folgende Befehle über die Kommandozeile auf:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README Fr (appendix/01/README-fr.md)
Comment exécuter les exemples sur un Raspberry Pi ?
Il y a quelques années, supposer que tout le monde dispose d'un ordinateur doté d'une unité de traitement graphique était ambitieux. Maintenant, la plupart des ordinateurs possèdent un GPU, mais c'est toujours une exigence compliqué dans un atelier ou une classe, par exemple.
Grâce à la Fondation Raspberry Pi, une nouvelle génération d'ordinateurs peu coûteux (environ 30€ l'unité) a trouvé son chemin dans les salles de classe. Plus important encore pour le propos de ce livre, le Raspberry Pi est livré avec un bon GPU Broadcom qui peut être utilisé directement depuis la console. J'ai créé un outil flexible de développement en temps réel pour GLSL appelé glslViewer qui exécute tous les exemples de ce livre. Ce programme a également la capacité de s'actualiser automatiquement lorsque l'utilisateur enregistre une modification de son code. Qu'est-ce que cela veut dire ? Vous pouvez modifier le shader et chaque fois que vous l'enregistrez, le shader sera re-compilé et rendu pour vous.
En effectuant une copie locale du dépôt git de ce livre (voir la section ci-dessous) et avec
glslViewer installé, les utilisateurs peuvent exécuter les exemples avec glslviewer. De plus, en utilisant l'option -l, ils peuvent lancer les exemples dans un coin de l'écran pendant qu'ils les modifient avec n'importe quel éditeur de texte (comme nano, pico, vi, vim ou emacs). Cela fonctionne également si l'utilisateur est connecté via ssh/sftp.Pour installer et configurer tout cela sur Raspberry Pi, après l'installation de Raspbian, une distribution Linux basée sur Debian conçue pour Raspberry Pi, connectez-vous et tapez les commandes suivantes :
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README Id (appendix/01/README-id.md)
Bagaimana cara menjalankan contoh di Raspberry Pi?
Beberapa tahun yang lalu, asumsikan bahwa setiap orang memiliki komputer dengan unit pemrosesan grafis adalah hal yang mustahil. Sekarang, sebagian besar komputer memiliki GPU, tetapi masih merupakan standar yang tinggi untuk persyaratan di bengkel atau kelas, misalnya.
Terima Kasih kepada Raspberry Pi Foundation sebuah jenis baru dari generasi komputer yang kecil dan murah (sekitar $35/Rp500,000 satunya) telah menemukan jalannya ke kelas. Yang terpenting untuk tujuan buku ini, Raspberry Pi hadir dengan GPU Broadcom yang layak yang dapat diakses langsung dari konsol. Saya membuat panggilan alat pengkodean langsung GLSL fleksibel glslViewer yang menjalankan semua contoh dalam buku ini. Program ini juga memiliki kemampuan untuk memperbarui secara otomatis ketika pengguna menyimpan perubahan pada kode mereka. Apa artinya ini? Anda dapat mengedit shader dan setiap kali Anda menyimpannya, shader akan dikompilasi ulang dan dirender untuk Anda.
Dengan membuat salinan lokal dari repositori buku ini (lihat bagian di atas) dan
glslViewer diinstal, pengguna dapat menjalankan contoh dengan glslviewer. Juga dengan menggunakan flag -l mereka dapat membuat contoh di sudut layar sementara mereka memodifikasinya dengan editor teks apa pun (seperti nano, pico, vi, vim atau emacs) . Ini juga berfungsi jika pengguna terhubung melalui ssh/sftp.Untuk memasang dan mengatur semua ini pada Raspberry Pi setelah memasang Raspbian, sebuah distribusi Linux berbasis Debian yang dibuat untuk Raspberry Pi, ketik perintah berikut:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README It (appendix/01/README-it.md)
Come posso eseguire gli esempi su un Raspberry Pi?
Alcuni anni fa, non era del tutto ovvio che tutti disponessero di un computer con una GPU. Ora, nonostante la maggior parte dei computer abbia una carta grafica, non tutti i corsi o classi possono permettersene uno.
Grazie al progetto Raspberry Pi una nuova generazione di computer piccoli ed economici (circa 35euro ciascuno) ha cominciato a diffondersi nelle aule scolastiche. Ancora più importante ai fini di questo libro, il Raspberry Pi viene fornito con una scheda GPU Bradcom decente a cui si può accedere direttamente dalla console. Ho creato uno strumento flessibile per programmare GLSL live chiamato glslViewer capace di far funzionare tutti gli esempi di questo libro. Questo programma è anche capace di aggiornare automaticamente le modifiche che l'utente fa quando le salva. Cosa significa? è possibile modificare lo shader e ogni volta che lo si salva, lo shader sarà ricompilato e visualizzato per voi.
Facendo una copia locale del repository di questo libro (vedi paragrafo precedente) e con
`glslViewer` installato, gli utenti possono eseguire gli esempi con `glslviewer`. Inoltre usando il flag `-l` si può visualiizare l'esempio in un angolo dello schermo mentre lo si modifica con un qualsiasi editor di testo (come `nano`, `pico`, `vi`, `vim` or `emacs`). Questo funziona anche quando l'utente è collegato tramite ssh/sftp.Per installare e configurare tutto ciò sul Raspberry Pi, dopo l'installazione del sistema operativo e il login, digitate i seguenti comandi:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README Pl (appendix/01/README-pl.md)
Jak uruchomić przykłady na Raspberry Pi?
Jeszcze kilka lat temu założenie, że każdy ma komputer z procesorem graficznym było dalekie od prawdy. Teraz większość komputerów ma GPU, ale to wciąż wysoka poprzeczka.
Dzięki Fundacji Raspberry Pi nowy typ małych i tanich komputerów nowej generacji (około 35 dolarów za sztukę) trafił do sal lekcyjnych. Co ważniejsze dla celów tej książki, Raspberry Pi jest wyposażone w przyzwoity procesor graficzny Broadcom, do którego można uzyskać dostęp bezpośrednio z konsoli. Stworzyłem elastyczne narzędzie do kodowania GLSL na żywo o nazwie glslViewer, które uruchamia wszystkie przykłady zawarte w tej książce. Program ten automatycznie się odswieża, gdy użytkownik zapisze zmiany w swoim kodzie. Co to oznacza? Możesz edytować shader i za każdym razem, gdy go zapiszesz, shader zostanie ponownie skompilowany i wyrenderowany za Ciebie.
Robiąc lokalną kopię repozytorium tej książki (zobacz poprzedni rozdział) i mając zainstalowany
glslViewer, użytkownicy mogą uruchamiać przykłady za pomocą glslviewer. Dodając flagę -l mogą oni renderować przykład w rogu ekranu, podczas gdy modyfikują go za pomocą dowolnego edytora tekstu (jak nano, pico, vi, vim lub emacs). Działa to również przy połączeniu przez ssh/sftp.Aby zainstalować i skonfigurować to wszystko na Raspberry Pi, po zainstalowaniu Raspbian (dystrybucja Linuksa oparta na Debianie, stworzona dla Raspberry Pi) i zalogowaniu się, wpisz następujące polecenia:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README Ru (appendix/01/README-ru.md)
Как запустить примеры на Raspberry Pi?
Несколько лет назад было бы слишком опрометчиво предположить, что у каждого есть компьютер с графическим ускорителем. Теперь же большинство компьютеров содержат GPU, но требование обязательного его наличия является завышенным для учебной лаборатории или класса.
Благодаря Raspberry Pi Foundation, в учебных классах появился новый тип небольших и дешёвых компьютеров (около $35 за штуку). Что более важно для данной книги, Raspberry Pi поставляется с приличным GPU фирмы Broadcom, который доступен напрямую из консоли. Я написал гибкий инструмент для программирования на GLSL в реальном времени под названием glslViewer. С его помощью можно запустить все примеры из этой книги. Эта программа может выполнять обновление автоматически когда пользователь сохраняет изменения в коде. Что это означает? Каждый раз, когда вы сохраняете шейдер в процессе редактирования, он будет перезапущен и перерисует изображение.
Сделав локальную копию репозитория книги (см. предыдущий параграф) и установив
glslViewer, вы можете запустить примеры. Используя флаг -l, вы можете рендерить примеры в углу экрана прямо во время редактирования любым редактором (nano, pico, vi, vim или emacs). Это так же работает при подключении по ssh или sftp.Чтобы установить всё необходимое на Raspberry Pi, после установки Raspbian и входа в систему, выполните следующие команды:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README Tr (appendix/01/README-tr.md)
Örnekleri Raspberry Pi'da nasıl çalıştırırım?
Birkaç yıl önce, herkesin grafik işlem birimine sahip bir bilgisayarı olduğunu varsaymak çok iddialıydı. Artık çoğu bilgisayarın GPU'su var, ama yine de bir atölye veya sınıf için yüksek bir gereksinim.
Raspberry Pi Vakfı sayesinde yeni nesil küçük ve ucuz bilgisayarlar (her biri yaklaşık 35$) sınıflara girmiştir. Bu kitabın amaçları açısından daha da önemli olarak, Raspberry Pi konsoldan doğrudan erişilebilen düzgün bir Broadcom GPU ile birlikte gelir. Bu kitaptaki tüm örnekleri çalıştıran glslViewer adlı esnek bir GLSL canlı kodlama aracı yaptım. Bu program ayrıca kullanıcı kodunda bir değişiklik kaydettiğinde otomatik olarak güncelleme yapabilir. Bu ne anlama geliyor? Shader'ı düzenleyebilirsiniz ve her kaydettiğinizde shader yeniden derlenecek ve sizin için render edilecektir.
Bu kitabın deposunun yerel bir kopyasını yaparak (yukarıdaki bölüme bakın) ve
glslViewer yükleyerek, kullanıcılar örnekleri glslviewer ile çalıştırabilir. -l bayrağını kullanarak, örneği herhangi bir metin editörüyle (nano, pico, vi, vim veya emacs gibi) değiştirirken ekranın bir köşesinde render edebilirler. Bu, kullanıcı ssh/sftp üzerinden bağlıysa da çalışır.Raspberry Pi'da Raspbian yükledikten ve giriş yaptıktan sonra tüm bunları kurmak ve ayarlamak için aşağıdaki komutları yazın:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/01/README Vi (appendix/01/README-vi.md)
Làm thế nào để chạy các ví dụ trên Raspberry Pi ?
Vài năm trước, việc một ai đó sở hữu một chiếc máy tính có card đồ hoạ còn khá hiếm. Giờ thì hầu nhưu máy nào cũng có, nhưng vẫn còn lâu nữa thì các lớp học mới được trang bị đầy đủ.
Nhờ có Raspberry Pi Foundation, một thế hệ máy tính mới rất nhỏ và rẻ (chỉ khoảng 35$ một chiếc) đã ra đời và được trang bị cho các lớp học. Điều quan trọng nhất đối với quyển sách này là, các máy Raspberry Pi được trang bị GPU đời mới của hãng Broadcom sẽ cho phép truy cập trực tiếp ngay từ cửa sổ dòng lệnh. Tôi đã tạo ra một công cụ hỗ trợ code GLSL gọi là glslViewer có thể chạy tất cả các ví dụ trong quyển sách này. Công cụ này còn có khả năng cập nhật hình ảnh tự động, ngay khi bạn sửa code.
Bằng cách tạo một bản sao của quyển sách này ở máy tính của bạn (xem hướng dẫn ở phần trước) và cài đặt
glslViewer, bạn có thể chạy các ví dụ với glslViewer. Ngoài ra nếu thêm tham số -l khi chạy thì một góc màn hình sẽ được dùng để dựng hình ví dụ đó, trong khi bạn có thể dùng bất kỳ chương trình biên soạn nào để sửa code. Nó còn hoạt động ngay cả khi bạn kết nối từ một máy tính khác thông qua ssh/sftp.Để tạo bản sao của quyển sách và cài glslViewer sau khi đã có Raspbian, gõ các lệnh sau:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
---
Appendix/02/README (appendix/02/README.md)
How to print this book?
Let’s say you don’t want to navigate or interact with the examples and you just want a good old fashion text book which you can read on the beach or on your commute to the city. In that case you can print this book.
#### Installing glslViewer
For printing this book you need first to parse it. For that you will need
glslViewer a console shader tool that will compile and transform the shader examples into images.In MacOSX get sure to have homebrew installed and then on your terminal do:
bashbrew install glslviewer
On Raspberry Pi you need to get Raspbian, a Debian-based Linux distribution made for Raspberry Pi and then do:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Installing Python 3, Latex Engine and Pandoc
For parsing the Markdown chapters into Latex and then into a PDF file we will use Xetex Latex Engine and Pandoc.
In MacOSX:
Download and Install MacTeX by:
bashbrew install --cask mactex-no-gui
and then install Pandoc and Python 3 by:
bashbrew install pandoc python
On Raspberry Pi (Raspbian):
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Compile the book into a pdf and print it
Now that you have all you need, it is time to clone the repository of this book and compile the book.
For that open your terminal once again and type:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
If everything goes well, you will see a
book.pdf file which you can read on your favorite device or print.#### Compile the book into an epub for use with an e-reader
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
The generated
book.epub can be used directly, or converted to a .mobi file for use with Kindle by using a converter, for example Calibre.---
Appendix/02/README Ch (appendix/02/README-ch.md)
如何打印这本书?
假设你不想浏览示例或与示例进行交互,而只想要一本绝佳的、老派的、可以在海滩上或通勤到城市的路上阅读的纸质书。在这种情况下,你可以把这本书打印出来。
#### 安装glslViewer
glslViewer——一个控制台着色器工具,它将编译着色器示例并将其转换为图像。在 MacOSX 上请确保你已经安装了 homebrew 然后在你的终端上输入如下指令:
bashbrew install glslviewer
在 树莓派 上请确保你已经安装了 Raspbian, 一个为树莓派开发的基于Debian的Linux发行版本,然后执行如下指令:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### 安装Python 3,Latex Engine和Pandoc
为了将 Markdown 章节解析为 Latex,然后转换为PDF文件,我们将使用 Xetex Latex Engine 和 Pandoc。
在 MacOSX 上:
下载并安装 MacTeX :
bashbrew cask install mactex-no-gui
然后安装 Pandoc 和 Python 3:
bashbrew install pandoc python
在 树莓派 (Raspbian)上:
bashsudo apt-get install texlive-xetex pandoc python2.7
#### 将书编译成pdf并打印
现在您已经拥有了所需的一切,是时候克隆 本书的库 并编译本书了。
为此,请再次打开你的终端并输入:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
如果一切顺利,你将看到一个
book.pdf 文件,你可以在自己喜欢的设备上阅读或打印该文件。#### 将书编译成 epub 以供电子阅读器使用
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
生成的
book.epub 可以直接使用,也可以使用转换器(例如 Calibre)转换为 .mobi 文件以供 Kindle 阅读。---
Appendix/02/README De (appendix/02/README-de.md)
Wie kann ich dieses Buch ausdrucken?
Nehmen wir einmal an, Du willst gar nicht mit den Beispielprogrammen in diesem Buch interagieren, sondern den Text wie ein gutes altes Buch im Urlaub oder auf dem täglichen Weg zur Arbeit lesen. In diesem Fall kannst Du Dir den Text einfach ausdrucken.
#### Installation des glslViewer
Um den Text auszudrucken, müssen die verschiedenen Bestandteile des Buches zunächst aufbereitet werden. Dafür benötigst Du den
`glslViewer`, ein Werkzeug für die Kommandozeile, mit dem Du aus den verschiedenen Beispielprogrammen die daraus resultierenden Bilder generieren kannst.Unter MacOSX benötigst Du zunächst homebrew. Nach dessen Installation kannst Du in einem Terminalfenster folgendes eingeben:
bashbrew update
brew upgrade
brew tap homebrew/versions
brew install glfw3
cd ~
git clone http://github.com/patriciogonzalezvivo/glslViewer.git
cd glslViewer
make
make install
Auf einem RaspberryPi gibst Du folgende Befehle ein:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Installation von Latex und Pandoc
Für den Ausdruck müssen die einzelnen Kapitel zunächst aus dem vorliegenden Textformat (Markdown) zunächst nach Latex und anschließend nach PDF konvertiert werden.
Unter MacOSX:
Lade und installiere zunächst basictex inkl. der MacTeX-Erweiterungen und installiere anschließend Pandoc, indem Du folgende Kommandos eingibst:
bashbrew install pandoc
Auf einem RaspberryPi gibst Du bitte folgende Kommandos ein:
bashsudo apt-get install texlive-xetex pandoc
#### Das Buch nach PDF umwandeln und ausdrucken
Jetzt, wo Du alle Werkzeuge beisammenhast, ist es an der Zeit, eine Kopie des Buches aus der Ablage im Internet herunterzuladen und daraus eine PDF-Datei zu erstellen.
Öffne dafür erneut ein Terminalfenster und gib die folgenden Befehle ein:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make
Wenn alles funktioniert hat, findest Du anschließend die Datei
`book.pdf` vor, die Du auf Deinem bevorzugten Gerät lesen oder ausdrucken kannst.---
Appendix/02/README Fr (appendix/02/README-fr.md)
Comment imprimer ce livre ?
Disons que vous ne voulez pas naviguer ou interagir avec les exemples et que vous voulez juste un bon livre papier à l'ancienne, que vous pourrez lire sur la plage ou sur votre trajet vers la ville. Dans ce cas, vous pouvez imprimer ce livre.
#### Installation de glslViewer
Pour imprimer ce livre, il faut d'abord l'analyser. Pour cela, vous aurez besoin de
glslViewer un outil en ligne de commande qui compile et transforme les exemples de shaders en images.Sous MacOSX, assurez-vous d'avoir installé homebrew et ensuite dans votre terminal :
bashbrew update
brew upgrade
brew tap homebrew/versions
brew install glfw3
cd ~
git clone http://github.com/patriciogonzalezvivo/glslViewer.git
cd glslViewer
make
make install
Sur Raspberry Pi, vous devez installer Raspbian, une distribution Linux basée sur Debian conçu pour Raspberry Pi puis de lancer :
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Installation de Python 3, Latex et Pandoc
Pour analyser les chapitres Markdown dans Latex, puis dans un fichier PDF, nous utiliserons Xetex Latex Engine et Pandoc.
Sous MacOSX:
Téléchargez et installez basictex & MacTeX-Additions, puis installez Pandoc et Python avec :
bashbrew install pandoc python2.7
Sur Raspberry Pi (Raspbian):
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Compilez le livre dans un document pdf et imprimez-le
Maintenant que vous avez tout ce dont vous avez besoin, il est temps de cloner le dépôt git de ce livre et de compiler le livre.
Pour cela, ouvrez votre terminal une fois de plus et tapez :
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make
Si tout se passe bien, vous verrez un fichier
book.pdf que vous pouvez lire sur votre appareil préféré ou imprimer.---
Appendix/02/README Id (appendix/02/README-id.md)
Bagaimana mencetak buku ini?
Katakanlah anda tidak ingin melihat atau berinteraksi dengan contoh dan Anda hanya ingin buku teks mode lama yang bagus yang dapat Anda baca di pantai atau dalam perjalanan Anda ke kota. Dalam hal ini, Anda dapat mencetak buku ini.
#### Memasang glslViewer
Untuk mencetak buku ini anda terlebih dahulu harus menguraikannya. Untuk itu anda akan membutuhkan
glslViewer sebuah alat shader konsol yang akan mengkompilasi dan mengubah contoh shader ke gambar.Pada MacOSX, pastikan mempunyai homebrew yang terpasang dan terminal lakukanlah:
bashbrew install glslviewer
Pada Raspberry Pi, anda harus mempunyai Raspbian, sebuah distribusi Linux berbasis Debian yang dibuat untuk Raspberry Pi dan lakukanlah:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Memasang Python 3, Latex Engine dan Pandoc
Untuk mengurai bab Markdown ke Latex dan kemudian ke berkas PDF, kita akan menggunakan Xetex Letex Engine dan Pandoc
Pada MacOSX:
Unduh dan Pasang MacTeX dengan:
bashbrew install --cask mactex-no-gui
Kemudian pasang Pandoc dan Python 3 dengan:
bashbrew install pandoc python
Pada Raspberry Pi (Raspbian):
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Compile the book into a pdf and print it
Sekarang anda sudah mempunya apa yang dibutuhkan, waktunya untuk mengkloning repositori buku ini) dan kompilasi buku ini:
Untuk itu buka terminal sekali lagi dan ketik:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
Jika semua berjalan lancar, anda akan melihat berkas
book.pdf yang bisa anda baca di perangkat favorit atau dicetak.#### Kompilasi buku menjadi epub untuk digunakan dengan e-reader
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
Book.epub yang dihasilkan dapat digunakan secara langsung, atau diubah menjadi file .mobi untuk digunakan dengan Kindle dengan menggunakan konverter, misalnya Calibre.---
Appendix/02/README It (appendix/02/README-it.md)
Come posso stampare questo libro?
Diciamo che non si vuole navigare o interagire con gli esempi e si desidera solo un buon vecchio libro di testo che si può leggere sulla spiaggia o sul vostro tragitto verso la città. In questo caso è possibile stampare questo libro.
#### Installare glslViewer
Per la stampa di questo libro è necessario in primo luogo trasformarlo. Per questo è necessario
`glslViewer` uno strumento console per gli shader che compilerà e trasformare gli esempi in immagini.Su MacOSX siate sicuri di avere installato homebrew e quindi sul terminale digitate:
bashbrew update
brew upgrade
brew tap homebrew/versions
brew install glfw3
cd ~
git clone http://github.com/patriciogonzalezvivo/glslViewer.git
cd glslViewer
make
make install
Su Raspberry Pi è necessario fare:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Installare Latex Engine e Pandoc
Per trasformare i capitoli Markdown in Latex e poi in un file PDF useremo Xetex Latex Engine e Pandoc.
Su MacOSX:
Scarica e Installa basictex & MacTeX-Additions e poi installa Pandoc facendo:
bashbrew install pandoc
Su Raspberry Pi:
bashsudo apt-get install texlive-xetex pandoc
#### Trasforma il libro in un pdf e stampalo
Ora che avete tutto ciò che serve, è il momento di clonare la repository di questo libro e compilare il libro.
A tale scopo aprite il terminale e digitate:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make
Se tutto va bene, si vedrà un file `book.pdf` che potete leggere sul vostro dispositivo preferito o stampare.---
Appendix/02/README Pl (appendix/02/README-pl.md)
Jak wydrukować tę książkę?
Powiedzmy, że nie chcesz nawigować ani wchodzić w interakcje z przykładami i chcesz po prostu mieć starą dobrą książkę tekstową, którą możesz czytać na plaży lub podczas dojazdu do miasta. W takim przypadku możesz wydrukować tę książkę.
#### Instalacja programu glslViewer
Aby wydrukować tę książkę, musisz najpierw ją przetworzyć. W tym celu będziesz potrzebował
glslViewer - konsolowego narzędzia do shaderów, które skompiluje i przekształci przykłady shaderów w obrazy.W MacOSX upewnij się, że masz zainstalowany homebrew, a następnie w terminalu wykonaj:
bashbrew install glslviewer
Na Raspberry Pi należy pobrać Raspbian, dystrybucję Linuksa opartą na Debianie, stworzoną dla Raspberry Pi, a następnie wykonać:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Instalacja Pythona 3, silnika Latex i Pandoc
Do parsowania Markdowna do Latexa, a następnie do pliku PDF użyjemy Xetex Latex Engine i Pandoc.
W MacOSX:
Pobierz i zainstalluj MacTeX:
bashbrew cask install mactex-no-gui
a następnie zainstalowuj Pandoc i Python 3 przez:
bashbrew install pandoc python
Na Raspberry Pi (Raspbian):
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Skompiluj książkę do pdf i wydrukuj ją
Teraz, gdy masz już wszystko, czego potrzebujesz, nadszedł czas na sklonowanie repozytorium tej książki i skompilowanie książki.
W tym celu otwórz jeszcze raz swój terminal i wpisz:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
Jeśli wszystko pójdzie dobrze, zobaczysz plik
book.pdf, który możesz przeczytać na swoim ulubionym urządzeniu lub wydrukować.#### Skompiluj książkę do postaci epub, aby użyć jej w e-czytniku.
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
Wygenerowany
book.epub może być użyty bezpośrednio, lub przekonwertowany, za pomomocą, na przykład, Calibre, na plik .mobi do użytku z Kindle.---
Appendix/02/README Ru (appendix/02/README-ru.md)
Как напечатать книгу?
Допустим, вам не нужна навигация по тексту или взаимодействие с примерами, и вы хотите просто почитать книгу на пляже или по пути в город. В таком случае вы можете напечатать книгу.
#### Установка glslViewer
Чтобы напечатать книгу, её нужно сначала распарсить. Для этого потребуется
glslViewer - консольный инструмент, который скомпилирует примеры шейдеров и преобразует их в изображения.На MacOSX убедитесь, что у вас есть homebrew, и выполните в терминале следующее:
bashbrew update
brew upgrade
brew tap homebrew/versions
brew install glfw3
cd ~
git clone http://github.com/patriciogonzalezvivo/glslViewer.git
cd glslViewer
make
make install
На Raspberry Pi установите Raspbian - дистрибутив Linux для Raspberry Pi, основанный на Debian, и выполните:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Установка Python 3, Latex Engine и Pandoc
Для разбора Markdown-разметки параграфов в Latex и затем в PDF, воспользуемся Xetex и Pandoc.
На MacOSX:
Скачайте и установите basictex & MacTeX-Additions, затем установите Pandoc и Python с помощью команды:
bashbrew install pandoc python2.7
На Raspberry Pi (Raspbian):
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Компиляция книги в pdf и печать
Когда всё необходимое установлено, склонируйте репозиторий книги и скомпилируйте её:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make
Если всё прошло хорошо, вы увидите файл
book.pdf, который можно прочитать на любом устройстве или распечатать.---
Appendix/02/README Tr (appendix/02/README-tr.md)
Bu kitabı nasıl yazdırabilirim?
Diyelim ki örneklerle gezinmek veya etkileşim kurmak istemiyorsunuz ve sadece sahilde veya şehre yolculuğunuzda okuyabileceğiniz güzel eski moda bir ders kitabı istiyorsunuz. Bu durumda bu kitabı yazdırabilirsiniz.
#### glslViewer kurulumu
Bu kitabı yazdırmak için önce onu ayrıştırmanız gerekir. Bunun için shader örneklerini derleyip görüntülere dönüştürecek bir konsol shader aracı olan
glslViewer'a ihtiyacınız olacak.MacOSX'te homebrew yüklü olduğundan emin olun ve ardından terminalinizde şunları yapın:
bashbrew install glslviewer
Raspberry Pi'da, Raspberry Pi için yapılmış Debian tabanlı bir Linux dağıtımı olan Raspbian'ı edinip ardından şunları yapmanız gerekir:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Python 3, Latex Motoru ve Pandoc kurulumu
Markdown bölümlerini Latex'e ve ardından PDF dosyasına ayrıştırmak için Xetex Latex Motorunu ve Pandoc'u kullanacağız.
MacOSX'te:
MacTeX'i indirip kurun:
bashbrew install --cask mactex-no-gui
ardından Pandoc ve Python 3'ü kurun:
bashbrew install pandoc python
Raspberry Pi (Raspbian)'da:
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Kitabı pdf'e derleyin ve yazdırın
İhtiyacınız olan her şeye sahip olduğunuza göre, bu kitabın deposunu klonlamanın ve kitabı derlemenin zamanı geldi.
Bunun için terminalinizi tekrar açın ve yazın:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
Her şey yolunda giderse, favori cihazınızda okuyabileceğiniz veya yazdırabileceğiniz bir
book.pdf dosyası göreceksiniz.#### Kitabı e-okuyucu için epub formatında derleyin
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
Oluşturulan
book.epub doğrudan kullanılabilir veya bir dönüştürücü kullanılarak Kindle ile kullanım için .mobi dosyasına dönüştürülebilir, örneğin Calibre.---
Appendix/02/README Ua (appendix/02/README-ua.md)
Як надрукувати цю книгу?
Скажімо, вам не потрібна навігація по тексту чи взаємодія з інтерактивними прикладами, а просто потрібна книга, яку ви зможете читати на пляжі або під час поїздок. У такому випадку ви можете надрукувати її.
#### Встановлення glslViewer
Щоб надрукувати цю книгу, її потрібно спочатку розпарсити. Для цього вам знадобиться
glslViewer - консольний інструмент, який компілює та перетворює приклади шейдерів у зображення.На MacOSX переконайтесь, що у вас встановлено homebrew і потім виконайте у терміналі наступну команду:
bashbrew install glslviewer
На Raspberry Pi установіть Raspbian - дистрибутив Linux на основі Debian, створений для Raspberry Pi, а потім виконайте такі дії:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Встановлення Python 3, Latex Engine і Pandoc
Для розбору розділів Markdown-розмітки у Latex, а потім у PDF-файл, ми будемо використовувати Xetex Latex Engine і Pandoc.
На MacOSX:
Завантажте та встановіть MacTeX:
bashbrew install --cask mactex-no-gui
а потім інсталюйте Pandoc і Python 3 за допомогою команди:
bashbrew install pandoc python
На Raspberry Pi (Raspbian):
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Зберіть книгу у pdf-формат та роздрукуйте її
Тепер, коли у вас є все необхідне, настав час клонувати репозиторій цієї книги та скомпілювати його.
Для цього ще раз відкрийте термінал і виконайте наступні команди:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
Якщо все пройде добре, ви побачите файл
book.pdf, який можна прочитати на своєму улюбленому пристрої або роздрукувати.#### Зберіть книгу в epub-формат для використання з Kindle
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
Згенерований файл
book.epub можна використовувати безпосередньо або конвертувати у формат .mobi для використання з Kindle за допомогою конвертера, наприклад Calibre.---
Appendix/02/README Vi (appendix/02/README-vi.md)
Làm thế nào để in quyển sách này
Giả sử bạn không có nhu cầu tương tác với các ví dụ mà chỉ muốn đọc quyển sách theo cách cổ điển như khi đang nằm trên bãi biển hoặc trên tàu điện. Trong trường hợp đó bạn có thể in quyển sách này.
#### Cài đặt glslViewer
Để in quyển sách này đầu tiên phải biên dịch nó đã. Để làm được việc đó thì bạn cần
glslViewer để biến các đoạn code shader thành ảnh minh hoạ.Trên MacOSX nếu đã có homebrew thì cần gõ lệnh sau:
bashbrew install glslviewer
Trên Raspberry Pi bạn cần cài Raspbian, một phiên bản Linux dựa trên Debian dành riêng cho Raspberry PI rồi gõ lệnh sau:
bashsudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
#### Cài đặt Python 2.7, Latex Engine và Pandoc
Để biên dịch các chương sách viết theo cú pháp Markdown bằng phần mềm Latex rồi xuất ra định dạng PDF thì bạn cần có Latex Engine và Pandoc.
Trên MacOSX:
Tải và cài đặt MacTeX bằng lệnh:
bashbrew install --cask mactex-no-gui
sau đó cài thêm Pandoc và Python 2 bằng lệnh:
bashbrew install pandoc python@2
Trên Raspberry Pi (Raspbian):
bashsudo apt-get install texlive-xetex pandoc python2.7
#### Chuyển đổi quyển sách sang định dạng PDF
Giờ bạn đã có đủ công cụ cần thiết, hãy tạo một bản sao của quyển sách này và in nó thôi.
Ở cửa sổ terminal, hãy gõ lệnh:
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
Nếu mọi thứ ổn, bạn sẽ thấy file
book.pdf mà bạn có thể đọc bằng bất kỳ thiết bị nào hoặc in ra.#### Chuyển đổi quyển sách sang định dạng epub để đọc trên thiết bị kỹ thuật số
bashcd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
File
book.epub có thể đọc trực tiếp hoặc cần chuyển sang định dạng .mobi để đọc trên Kindle bằng một phần mềm khác như Calibre chẳng hạn.---
Appendix/03/README (appendix/03/README.md)
How can I collaborate with this book?
Thanks for being willing to collaborate! There are plenty of ways you can:
`glossary/` section
- Editing content
- Sharing your shaders examples through the on-line editor toTranslating content
This book is written in Markdown language so it's very easy to edit and work on it.
1. Start by going to github's repository at
`github.com/patriciogonzalezvivo/thebookofshaders`. Take a look at the files and folders inside it. You will note that the content is in the `README.md` and other files with capital letters like: `TITLE.md`, `SUMMARY.md`, etc. Also note that translations are hosted in files with names ending in two letters referencing the language they are for, ex.: `README-jp.md`, `README-es.md`, etc.2. Fork the repository and clone it in your computer.
3. Duplicate the content of the files want to translate. Remember to add to the two letters that makes reference to the language you are translating to the files you will work on.
4. Translate the content line by line (see Translation notes).
5. Test it (see Testing).
6. Push to your own github fork to then make a Pull Request
#### Translating notes
Do not erase or modify things the embedded examples, that looks like this:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
or
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Testing
Start running a local PHP server inside the local repository folder:
bashphp -S localhost:8000
Then in your browser search for
`localhost:8000` go to the chapter you are translating and add `?lan=` followed by the two letters you used to mark the language you are translating to.For example, if you are translating the chapter
`03` to french you had been working with the file `03/README-fr.md` and you can test it by going to: `http://localhost:8000/03/?lan=frtextImproving the glossary section
This section is under development. We are happy to listen to your ideas on how to make it a friendly tool for all. Send us a message to @bookofshaders.
Editing content
We are all humans. If you see something say something and make a Pull Request or open an issue. Thanks!
Sharing your shaders examples
You will see a lot of links to the on-line editor and embedded instances of it.
Once you code something that makes you proud, click the "Export" (or the
⇪` icon) and then copy the "URL to code...". Send it to @bookofshaders or @kyndinfo. We are looking forward to see it and add it to the example gallery section.---
Appendix/03/README De (appendix/03/README-de.md)
Wie kann ich zu diesem Buch beitragen?
Schön, dass Du Interesse hast, an diesem Buch mitzuwirken. Zahlreiche Möglichkeiten bieten sich dafür an. Du kannst ...
`Glossar` verbessern
- Fehler bereinigen
- Deine eigenen GLSL-Shader über den Online Editor mit anderen teilenInhalte übersetzen
Der Quelltext dieses Buches wurde in Markdown verfasst. Dadurch ist es sehr einfach, den Text zu bearbeiten und zu erweitern.
1. Besuche zunächst die Internet-Ablage dieses Buches bei Github unter
`github.com/patriciogonzalezvivo/thebookofshaders`. Verschaffe Dir einen Überblick über die verschiedenen Verzeichnisse und die darin enthaltenen Dateien. Du wirst feststellen, dass die eigentlichen Textinhalte des Buches jeweils in der Datei `README.md` und in anderen Dateien enthalten sind, die Großbuchstaben im Namen tragen. Beispiele dafür sind etwa `TITLE.md`, `SUMMARY.md` usw. Die Übersetzungen dieser Texte stecken in Dateien, die am Ende des Namens ein Kürzel für die jeweilige Sprache tragen, also z.B. `README-jp.md`, `README-es.md` usw.2. Lege einen Fork der Ablage an und klone den Fork auf Deinen Computer.
3. Lege jeweils eine Kopie der Datei an, die Du übersetzen möchtest, und vergiss dabei nicht, das Sprachkürzel im Dateinamen zu ergänzen.
4. Übersetze den Inhalt des Dokuments Zeile für Zeile (siehe dazu auch die Übersetzungshinweise unten).
5. Überprüfe den Inhalt hinsichtlich der Rechtschreibung und der korrekten Anzeige im Browser (siehe unten unter Testen).
6. Lade die veränderten Dateien in Deinen Fork hoch und setze einen Pull Request ab.
#### Übersetzungshinweise
Lösche und verändere nichts an der Einbindung der Beispielprogramme innerhalb des Quelltextes. Dieses sehen wie folgt aus:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
oder
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Testen
Starte auf Deinem lokalen Rechner einen PHP-Server innerhalb des Verzeichnisses, in das Du das Buch geklont hast:
bashphp -S localhost:8000
Rufe dann in Deinem Internet-Browser die URL
`localhost:8000` auf. Klicke Dich bis zu dem Kapitel durch, an dem Du gerade arbeitest. Füge der angezeigten URL in der Adresszeile des Browsers den Zusatz `?lan=` hinzu, gefolgt von dem Sprachenkürzel, das Du bei der Benennung der jeweiligen Datei verwendet hast.Um ein Beispiel zu nennen: Wenn Du gerade das Kapitel
`03` ins Französische übersetzt und dabei mit der Datei `03/README-fr.md` arbeitest, kannst Du diese Datei anzeigen, indem Du folgende URL eingibst: `http://localhost:8000/03/?lan=frtextVerbesserungen am Glossar
Das Glossar befindet sich noch in Arbeit. Wir freuen uns über Vorschläge, wie man das Glossar zu einem wertvollen Bestandteil des Buches ausbauen kann. Schick uns einfach eine Nachricht an @bookofshaders.
Fehler bereinigen
Irren ist menschlich! Wenn Dir ein Fehler auffällt, bereinige die Stelle und setze einfach einen entsprechenden Pull Request auf Github ab, oder eröffnete dort eine Diskussion zu dem Thema. Vielen Dank!
Teile Deine Shader mit Anderen
Du wirst im Quelltext des Buches bei den Beispielen viele Verweise auf den Online-Editor sehen.
Sobald Du einen Shader entwickelt hast, auf den Du stolz bist, klicke auf die „Export“-Schaltfläche (bzw. das
⇪`-Symbol) und kopiere anschließend die im Editor angezeigte „URL to code...“. Sende diese URL an @bookofshaders oder an @kyndinfo. Wir freuen uns auf tolle Shader und werden diese gerne unserer Galerie mit Beispielen hinzufügen.---
Appendix/03/README Id (appendix/03/README-id.md)
Bagaimana saya bisa berkolaburasi?
Terima kasih telah bersedia untuk berkolaborasi! Ada banyak cara yang bisa anda lakukan:
- Menerjemahkan konten
- Mengimprovisasi bagian glosarium (
`glossary/`)
- Menyunting konten
- Membagikan contoh shadermu melalui editor on-lineMenerjemahkan Konten
Buku ini dibuat dalam bahasa Markdown jadi ini sangat mudah untuk disunting dan dikerjakan.
1. Mulailah dengan membuka repositori Github di
`github.com/patriciogonzalezvivo/thebookofshaders`. Lihatlah file dan folder di dalamnya. Anda akan melihat bahwa isinya ada di `README.md` dan file lain dengan huruf kapital seperti: `TITLE.md`, `SUMMARY.md`, dll. Perhatikan juga bahwa terjemahan di-host dalam file dengan nama yang diakhiri dengan dua huruf yang merujuk pada bahasanya, mis .: `README-jp.md`, `README-es.md`, dll.2. Fork repositori dan klon di komputer Anda.
3. Gandakan konten file yang ingin diterjemahkan. Ingatlah untuk menambahkan dua huruf yang mengacu pada bahasa yang Anda terjemahkan ke file yang akan anda kerjakan.
4. Terjemahkan baris demi baris konten (lihat Catatan terjemahan).
5. Uji (lihat Pengujian).
6. Push ke fork github anda sendiri untuk kemudian buatlah Pull Request
#### Catatan Terjemahan
Jangan menghapus atau mengubah contoh yang disematkan, seperti:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
or
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Pengujian
Mulai jalankan server lokal PHP di dalam folder repositori lokal:
bashphp -S localhost:8000
Kemudian dalam browser cari
`localhost:8000`, pergi ke bab yang ingin diterjemahkan dan tambahkan `?lan=` diikuti dengan dua huruf yang anda gunakan untuk menandai bahasa yang anda terjemahkan.Mengimprovisasi bagian glosarium
Bagian ini dibawah pengembangan. Kami senang untuk mendengar idemu pada cara untuk membuat alat yang bersahabat untuk semua.
Menyunting konten
Kita semua manusia. Jika anda melihat sesuatu, lakukanlah sesuatu, buat Pull Request atau buka/buat isu. Terima Kasih!
Membagikan contoh shadermu
Anda akan melihat banyak tautan ke editor on-line dan contoh yang disematkan darinya.
Setelah anda membuat sesuatu yang membuat anda bangga, klik "Export" (atau ikon
`⇪`) dan kemudian salin "URL to code...", kirim ke @bookofshaders atau @kyndinfo. Kami sangat menantikan untuk melihatnya dan menambahkannya ke bagian galeri contoh---
Appendix/03/README It (appendix/03/README-it.md)
Come posso collaborare a questo libro?
Grazie per voler collaborare! Ci sono vari modi per poterlo fare:
- Tradurre i contenuti
- Migliorare la sezione
`glossario/`
- Modificare i contenuti
- Condividere i vostri esempi di shaders attraversol'editor on-lineTradurre i contenuti
Questo libro è scritto nel linguaggio Markdown quindi è molto facile da modificare e lavorare su di esso.
1. Iniziate andando alla repository di GitHub
`github.com/patriciogonzalezvivo/thebookofshaders`. Date un'occhiata ai file e alle cartelle al suo interno. Si noti che il contenuto è presente nei file `README.md` e negli altri con lettere maiuscole come:`TITLE.md`, `SUMMARY.md`, ecc. Si noti inoltre che le traduzioni sono contenute in file che terminano con due lettere che fanno riferimento alla lingua che sono tradotte, es .: `README-jp.md`, `README-es.md`, ecc.2. Biforcare ("Fork") la repository e clonatela ("Clone") sul vostro computer.
3. Duplicate il contenuto dei file da tradurre. Ricordatevi di aggiungere ai file su cui si sta lavorando le due lettere che fanno riferimento alla lingua che si sta traducendo.
4. Traducete linea per linea i contenuti (vedi Note di traduzione).
5. Testate le pagine tradotte (vedi Test).
6. Inviate ("Push") i vostri commit alla biforcazione ("Fork") della vostra repository su GitHub, per poi fare un Pull Request
#### Note di traduzione
Non cancellate o modificate gli esempi integrati alla pagina, che assomigliano a questo codice:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
oppure
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Testare
Iniziate l'esecuzione di un server locale PHP all'interno della cartella locale:
bashphp -S localhost:8000
Poi, nel tuo browser aprite la pagina
`localhost:8000`, andate al capitolo che state traducendo e aggiungete `?lan=` seguito dal codice della lingua in cui state traducendo.Per esempio se si sta traducendo il capitolo
`03` in francese, starete lavorando sul file`03/README-fr.md` e lo si può testare andando alla pagina: `http://localhost:8000/03/?lan=frtextMigliorare la sezione glossario
Questa sezione è in fase di sviluppo. Siamo lieti di ascoltare le vostre idee su come rendere uno strumento intuitivo per tutti. Inviaci un messaggio a @bookofshaders.
Modifica il contenuto
Siamo tutti esseri umani. Se vedete qualcosa, ditelo e fate un Pull Request oppure aprite un problema su GitHub. Grazie!
Condividete i vostri esempi di shaders
Vedrete un sacco di link verso l'editor on-line e verso delle sue istanze integrate alla pagina.
Una volta che si scrive un codice che vi rende orgoglioso, fate clic su "Esporta" (o sull' icona
⇪`) e quindi copiate l' "URL verso il codice..." ("URL to code..."). Inviatelo a @bookofshaders o a @kyndinfo. Non vediamo l'ora di vederlo e aggiungerlo alla sezione galleria di esempi.---
Appendix/03/README Pl (appendix/03/README-pl.md)
Jak mogę pomóc?
Dziękujemy za chęć współpracy! Jest wiele sposobów pomocy:
- Tłumaczenie treści
- Poprawianie sekcji
`glosariusz/`
- Edytowanie zawartości
- Dzielenie się swoimi przykładami shaderów poprzez edytor on-lineTłumaczenie treści
Ta książka jest napisana w języku Markdown, więc bardzo łatwo jest ją edytować i pracować nad nią.
1. Zacznij od przejścia do repozytorium github pod adresem
github.com/patriciogonzalezvivo/thebookofshaders. Przyjrzyj się znajdującym się w nim plikom i folderom. Zauważysz, że treść znajduje się w pliku README.md oraz innych plikach z dużymi literami jak: TITLE.md, SUMMARY.md, itd. Zauważ również, że tłumaczenia są hostowane w plikach z nazwami kończącymi się na dwie litery reprezentujące język tłumaczenia, na przykład: README-jp.md, README-es.md, itd.2. Forkuj repozytorium i sklonuj je w swoim komputerze.
3. Zduplikuj zawartość plików, które chcesz przetłumaczyć. Pamiętaj, aby do plików, nad którymi będziesz pracował, dodać dwie litery nawiązujące do języka, który tłumaczysz.
4. Przetłumacz treść linijka po linijce (patrz Uwagi dotyczące tłumaczenia).
5. Przetestuj ją (patrz Testy).
6. Pushuj na własny fork githuba, aby następnie zrobić Pull Request
#### Uwagi dotyczące tłumaczenia
Nie wymazuj ani nie modyfikuj rzeczy w osadzonych przykładach, wyglądających tak:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
lub
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Testowanie
Rozpocznij uruchamianie lokalnego serwera PHP wewnątrz lokalnego folderu repozytorium:
bashphp -S localhost:8000
Następnie w przeglądarce wyszukaj
localhost:8000, przejdź do rozdziału, który tłumaczysz i dodaj ?lan=, a następnie dwie litery, których użyłeś do oznaczenia języka, na który tłumaczysz.Na przykład, jeśli tłumaczysz rozdział
03 na język francuski pracowałeś z plikiem 03/README-fr.md, to możesz go przetestować wchodząc na: http://localhost:8000/03/?lan=frUlepszanie glosariusza
Glosariusz jest w trakcie rozwoju. Chętnie wysłuchamy Twoich pomysłów, jak uczynić ją przyjaznym narzędziem dla wszystkich. Wyślij nam wiadomość na adres @bookofshaders.
Edycja treści
Wszyscy jesteśmy ludźmi. Jeśli widzisz błąd, daj znać i zrób Pull Request lub otwórz Issue. Dzięki!
Dzielenie się przykładami shaderów
Zobaczysz wiele linków do edytora on-line i jego osadzonych instancji.
Gdy zakodujesz coś, co sprawi, że będziesz dumny, kliknij "Export" (lub ikonę
⇪), a następnie skopiuj "URL to code...". Wyślij go do @bookofshaders lub @kyndinfo. Czekamy na nie i dodamy je do działu galeria przykładów.---
Appendix/03/README Ru (appendix/03/README-ru.md)
Как принять участие в создании книги?
Благодарим за ваше желание помочь! Есть множество способов это сделать:
- Перевод
- Доработка глоссария
- Редактирование
- Выкладывание ваших шейдеров в общий доступ с помощью онлайн-редактора
Перевод
Книга написана на языке разметки Markdown, поэтому с её текстом очень легко работать.
`github.com/patriciogonzalezvivo/thebookofshaders`. Осмотрите файлы и каталоги в нём. Легко заметить, что контент находится в файлах `README.md` и других файлах, названных заглавными буквами: `TITLE.md`, `SUMMARY.md` и так далее. Так же вы заметите, что переводы находятся в файлах, имена которых заканчиваются двумя буквами, указывающими на язык перевода, например: `README-jp.md`, `README-es.md` и прочие.2. Форкните репозиторий и склонируйте его к себе на компьютер.
3. Продублируйте содержимое файлов, которые хотите перевести. Не забудьте добавить двухбуквенный код языка, на который переводите.
4. Переведите контент (см. Примечания о переводе).
5. Протестируйте (см. Тестирование).
6. Сделайте push в ваш собственный форк и затем создайте пулл-реквест в основной репозиторий.
#### Примечания о переводе
Не удаляйте и не изменяйте встроенные примеры, которые выглядят примерно так:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
или так:
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Тестирование
Запустите локальный PHP-сервер в папке вашего локального репозитория:
bashphp -S localhost:8000
Теперь в браузере перейдите по адресу
`localhost:8000`, зайдите на переводимую страницу и добавьте в конец адреса строку `?lan=xx`, где `xx` - код языка, на который переводите.Например, если вы переводите главу
`03` на французский, значит вы работали над файлом `03/README-fr.md`, который можно протестировать по адресу `http://localhost:8000/03/?lan=fr`.Доработка глоссария
Этот раздел находится в разработке. Мы рады узнать ваше мнение о том, как сделать из него что-то полезное. Пишите нам на @bookofshaders.
Редактирование
Все мы люди. Если вы видите ошибку - сообщите о ней и сделайте пулл-реквест, или откройте issue на гитхабе. Спасибо!
Выкладывайте ваши шейдеры
Вы увидите множество ссылок на онлайн-редактор и написанный в нём код, встроенный в книгу. Если вы написали что-то стоящее, нажмите «Export» (или иконку
`⇪`) и скопируйте ссылку на код. Отправьте её на @bookofshaders или @kyndinfo. Мы будем рады видеть ваш код и добавить его в галерею примеров.---
Appendix/03/README Tr (appendix/03/README-tr.md)
Bu kitaba nasıl katkıda bulunabilirim?
Katkıda bulunmaya istekli olduğunuz için teşekkürler! Birçok yol var:
`glossary/` bölümünü iyileştirme
- İçerik düzenleme
- Shader örneklerinizi çevrimiçi editör aracılığıyla paylaşmaİçerik çevirisi
Bu kitap Markdown dilinde yazılmıştır, bu yüzden düzenlemesi ve üzerinde çalışması çok kolaydır.
`github.com/patriciogonzalezvivo/thebookofshaders` adresindeki GitHub deposuna giderek başlayın. İçindeki dosya ve klasörlere göz atın. İçeriğin `README.md` ve `TITLE.md`, `SUMMARY.md` gibi büyük harfli diğer dosyalarda olduğunu göreceksiniz. Ayrıca çevirilerin, hedef dile referans veren iki harfle biten dosya adlarında barındırıldığını not edin, örn: `README-jp.md`, `README-es.md`, vb.2. Depoyu forklayın ve bilgisayarınıza klonlayın.
3. Çevirmek istediğiniz dosyaların içeriğini çoğaltın. Çevirdiğiniz dile referans veren iki harfi üzerinde çalışacağınız dosyalara eklemeyi unutmayın.
4. İçeriği satır satır çevirin (bkz. Çeviri notları).
5. Test edin (bkz. Test etme).
6. Kendi GitHub fork'unuza push edin ve ardından bir Pull Request yapın
#### Çeviri notları
Şöyle görünen gömülü örnekleri silmeyin veya değiştirmeyin:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
veya
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Test etme
Yerel depo klasörünün içinde yerel bir PHP sunucusu çalıştırmaya başlayın:
bashphp -S localhost:8000
Ardından tarayıcınızda
`localhost:8000` arayın, çevirdiğiniz bölüme gidin ve `?lan=` ardından çevirdiğiniz dili işaretlemek için kullandığınız iki harfi ekleyin.Örneğin,
`03` bölümünü Fransızca'ya çeviriyorsanız `03/README-fr.md` dosyasıyla çalışmış olacaksınız ve şu adrese giderek test edebilirsiniz: `http://localhost:8000/03/?lan=frtextSözlük bölümünü iyileştirme
Bu bölüm geliştirme aşamasındadır. Onu herkes için kullanıcı dostu bir araç yapma hakkındaki fikirlerinizi duymaktan mutluluk duyarız. Bize @bookofshaders adresinden mesaj gönderin.
İçerik düzenleme
Hepimiz insanız. Bir şey görürseniz bir şey söyleyin ve bir Pull Request yapın veya bir sorun açın. Teşekkürler!
Shader örneklerinizi paylaşma
Çevrimiçi editöre ve gömülü örneklerine birçok bağlantı göreceksiniz.
Sizi gururlandıran bir şey kodladığınızda, "Dışa Aktar" (veya
⇪` simgesi) düğmesine tıklayın ve ardından "Koda URL..."yi kopyalayın. @bookofshaders veya @kyndinfo adresine gönderin. Görmeyi ve örnek galeri bölümüne eklemeyi dört gözle bekliyoruz.---
Appendix/03/README Ua (appendix/03/README-ua.md)
Як прийняти участь у розвитку книги?
Дякуємо за бажання співпрацювати! Для цього існує кілька способів:
`глосарію`
- Редагування контенту
- Діліться своїми прикладами шейдерів через онлайн-редакторПереклад контенту
Ця книга написана мовою розмітки Markdown, тому її дуже легко редагувати та працювати з нею.
`github.com/patriciogonzalezvivo/thebookofshaders`. Перегляньте в ньому файли та теки. Ви помітите, що контент знаходиться в `README.md` та інших файлах з назвами із великих літер, наприклад: `TITLE.md`, `SUMMARY.md` тощо. Також зауважте, що переклади розміщуються у файлах, імена яких закінчуються додатковими двома літерами, що вказують на мову перекладу, наприклад: `README-jp.md`, `README-es.md` тощо.2. Зробіть відгалуження репозиторію і клонуйте його на свій комп'ютер.
3. Продублюйте вміст файлів, які потрібно перекласти. Додайте до них двобуквенний код, що буде вказувати на мову для якої ви робите переклад.
4. Перекладіть вміст файлу (див. Примітки щодо перекладу).
5. Протестуйте зміни (див. Тестування).
6. Зробіть push у ваш власний форк проєкту і створіть pull request
#### Примітки щодо перекладу
Не видаляйте та не змінюйте вбудовані приклади, які виглядають приблизно так:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
Або так:
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Тестування
Запустіть локальний PHP-сервер у теці локального репозиторію:
bashphp -S localhost:8000
`localhost:8000`, перейдіть до розділу, який ви перекладаєте, і додайте в кінці адреси такий рядок `?lan=`, а після нього дві літери для позначення мови перекладу.Наприклад, якщо ви перекладаєте розділ
`03` на французьку мову, значить ви працювали з файлом `03/README-fr.md`, і ви можете перевірити його за адресою: ` http://localhost:8000/03/?lan=frtextПокращення глосарію
Цей розділ знаходиться в розробці. Ми раді вислухати ваші ідеї щодо його покращення, щоб зробити його корисним інструментом для всіх. Надсилайте свої пропозиції на @bookofshaders.
Редагування контенту
Ми всі люди. Якщо ви побачите помилку, то сповістіть про неї, зробіть pull-request з виправленням або відкрийте issue. Дякую!
Діліться своїми прикладами шейдерів
Ви побачите багато посилань на онлайн-редактор і його вбудовані у сторінки приклади з кодом.
Коли ви закодуєте щось корисне чи цікаве, натисніть «Export» (або піктограму
⇪`) і надішліть його на адресу @bookofshaders або @kyndinfo. Ми будемо раді переглянути його і додати до галереї прикладів.---
Appendix/03/README Vi (appendix/03/README-vi.md)
Làm thế nào để đóng góp cho quyển sách này
Cảm ơn bạn vì muốn góp sức! Có rất nhiều cách để làm việc đó:
`Chú giải`
- Biên tập lại nội dung
- Chia sẻ code shader của bạn trên editor onlineDịch quyển sách sang ngôn ngữ khác
Quyển sách này được viết theo cú pháp Markdown nên rất dễ chỉnh sửa.
1. Bạn có thể truy cập repository
`github.com/patriciogonzalezvivo/thebookofshaders` trên GitHub. Hãy xem các file và thư mục ở đó. Bạn sẽ thấy nội dung của các chương nằm ở các file `README.md` và các file có tên viết hoa như: `TITLE.md`, `SUMMARY.md`, vân vân. Ngoài ra thì các bản dịch sẽ có file riêng và có tên kết thúc bằng mã ngôn ngữ 2 ký tự, ví dụ: `README-jp.md`, `README-es.md`, vân vân.2. Fork repository này rồi tạo một bản sao trên máy của bạn.
3. Tạo một bản sao cho mỗi file mà bạn muốn dịch. Hãy nhớ thêm mã ngôn ngữ 2 ký tự vào cuối tên file.
4. Dịch nội dung sang ngôn ngữ khác (xem mục Chú ý khi dịch thuật).
5. Kiểm tra lại (xem mục Kiểm tra).
6. Push lên repository riêng của bạn rồi tạo một Pull Request vào repository của chúng tôi.
#### Chú ý khi dịch thuật
Không được xoá hoặc thay đổi mã nhúng các ví dụ tương tự như các đoạn mã sau:
html<div class="codeAndCanvas" data="grid-making.frag"></div>
hoặc
html<div class="simpleFunction" data="y = mod(x,2.0);"></div>
#### Kiểm tra
Hãy thử chạy trên server PHP nội bộ của bạn:
bashphp -S localhost:8000
Rồi truy cập
`localhost:8000` trên trình duyệt và tới chương mà bạn đã dịch rồi thêm đuôi `?lan=` kèm mã ngôn ngữ 2 ký tự.Ví dụ, nếu bạn dịch chương
`03` sang tiếng Pháp tức là bạn sửa file `03/README-fr.md` nên giờ bạn sẽ tự kiểm tra lại tại địa chỉ: `http://localhost:8000/03/?lan=frtextCải thiện phần chú giải
Phần này vẫn đang được bổ sung. Chúng tôi rất vui lòng lắng nghe các ý tưởng của bạn để khiến nó trở nên dễ hiểu hơn. Hãy gửi tin nhắn tới @bookofshaders.
Biên tập nội dung
Chúng ta đều là những người bình thường có nhiều điểm có thể cải thiện hơn nữa. Nếu bạn có gì muốn góp ý, cứ tạo Pull Request hoặc một issue. Cảm ơn!
Chia sẻ code shader
Bạn sẽ thấy rất nhiều link trỏ tới editor online hoặc các phiên bản nhúng của nó ở quyển sách này.
Nếu bạn hoàn thành 1 đoạn code hay ho nào đó, hãy click nút "Export" (hoặc biểu tượng
⇪`) rồi copy "URL to code...". Sau đó gửi URL tới @bookofshaders hoặc @kyndinfo. Chúng tôi rất mong chờ được bổ sung nó vào phần thư viện các ví dụ.---
Appendix/04/README (appendix/04/README.md)
An introduction for those coming from JS
by Nicolas Barradeau
If you're a JavaScript developer, chances are you'll be a bit puzzled when reading the book.
Indeed, there are many differences between manipulating high-level JS and getting down and dirty with shaders.
Yet, as opposed to the underlying assembly language, GLSL is human readable and I'm sure that, once you acknowledge its specificities, you'll quickly be up and running.
I assume you have a prior (be it shallow) knowledge of JavaScript of course, but also of the Canvas API.
If not, don't worry, you'll still be able to get most of this section.
Also, I won't go too much into details and some things may be _half true_, don't expect a "definitive guide" but rather
A BIG HUG
JavaScript is great at quick prototyping ; you throw a bunch of random, untyped variables and methods, you can dynamically add and remove class members, refresh the page and see if it works,
make changes accordingly, refresh the page, repeat, life is easy.
So you may wonder what is the difference between JavaScript and GLSL.
After all, both run in the browser, both are used to draw a bunch of funky stuff on a screen and to that extent, JS is easier to use.
Well, the main difference is that Javascript is an interpreted language while GLSL is a compiled language.
A compiled program is executed natively on the OS, it is low level and generally fast.
An interpreted program requires a Virtual Machine (VM) to be executed, it is high level and generally slow.
When a browser (the _JavaScript VM_) executes or interprets a piece of JS, it has no clue about which variable is what and which function does what (with the notable exception of TypedArrays).
Therefore it can't optimize anything _upfront_, so it takes some time to read your code, to infer (deduce from the usage) the types of your variables and methods
and when possible, it will convert _some_ of your code into assembly code that will execute much faster.
It's a slow, painstaking and insanely complex process, if you're interested in the details, I'd recommend watching how Chrome's V8 engine works.
The worst is that every browser optimizes JS its way and the process is _hidden_ from you ; you are powerless.
A compiled program is not interpreted ; the OS runs it, if the program is valid, the program is executed.
That's a big change ; if you forget a semicolon at the end of line, your code is invalid, it will not compile: your code won't turn into a program at all.
That's cold but that's what a shader is: _a compiled program executed on the GPU_.
Fear not! a compiler, the piece of program that makes sure your code is valid, will become your best friend.
The examples of this book and the companion editor are very user friendly.
They'll tell you where and why your program failed to compile, then you'll have to fix things and whenever the shader is ready to compile, it will be displayed instantly.
That's a great way of learning as it's very visual and you can't really break anything.
Last note, a shader is made of 2 programs, the vertex shader and the fragment shader.
In a nutshell, the vertex shader, the first program, receives a geometry as an input and turns it into series of pixels (or fragments) then hands them over to the
fragment shader, the second program, that will decide which color to paint the pixels.
This book is mostly focused on the latter, in all the examples, the geometry is a simple quadrilateral that covers the whole screen.
SO! ready?
off we go!
Strong types
When you come from JS or any untyped language, typing your variables is an alien concept, making typing the hardest step to take towards GLSL.
Typing, as the name suggests, means that you'll give a type to your variables (and functions of course).
This basically means that the word
var doesn't exist anymore.
The GLSL thought-police erased it from the common tongue and you're not able to speak it because, well... it doesn't exist.Instead of using the magic word
var, you'll have to _explicitly specify the type of each variable_ you use, then the compiler will only see objects and primitives it knows how to handle efficiently.
The downside when you can't use the var keyword and must _specify everything_, is that you'll have to know the type of all the variables and know them well.
Rest assured, there are few and they're fairly simple (GLSL is not a Java framework).Might sound scary but all in all, it's not very different from what you're doing when you code JavaScript ; if a variable is a
boolean, you'll expect it to store true or false and nothing else.
If a variable is called var uid = XXX;, chances are that you'll store an integer value in there and a var y = YYY; _might_ be a reference to a floating point value.
Even better, with strong types, you won't waste time wondering if X == Y (or was it typeof X == typeof Y ? .. or typeof X !== null && Y... ... anyway) ; you'll just know it and if you don't, the compiler will.Here are the scalar types (a scalar describes a quantity) you can use in GLSL:
bool (Boolean), int(Integer), float(floating point Number).
There are other types but let's take it easy, the following snippet shows how to declare vars (yes, I spoke the forbidden word) in GLSL:
glsl//a Boolean value:
JS: var b = true; GLSL: bool b = true;//an Integer value
JS: var i = 1; GLSL: int i = 1;
//a Float value (a Number)
JS: var f = 3.14159; GLSL: float f = 3.14159;
Not that hard right? as mentioned above, it even makes things easier when it comes to coding as you don't waste your time checking the type of a given variable.
When in doubt, remember that you're doing this for your program to run immensely faster than in JS.#### void
There is a
void type that roughly corresponds to null, it is used as the return type of a method that doesn't return anything.
you can't assign it to a variable.#### boolean
As you know, Booleans are mostly used in conditional tests ;
if( myBoolean == true ){}else{}.
If the conditional branching is a valid option on the CPU, the parallel nature of GLSL makes it less true.
Using conditionals is even discouraged most of the time, the book explains a couple of alternative techniques to solve this.#### type casting
As Boromir put it, "One does not simply combine Typed primitives". Unlike JavaScript, GLSL will not allow you to perform operations between variables of different types.
This for instance:
glslint i = 2;
float f = 3.14159;//trying to multiply an integer by a float value
float r = i * f;
will not play nice because you're trying to crossbreed a _cat_ and a _giraffe_.
The solution to this is to use type casting ; it will _make the compiler believe_ that i is of type float without actually changing the type of i.
glsl//casting the type of the integer variable 'i' into float
float r = float( i ) * f;
Which is strictly equivalent to dressing up a _cat_ in a _giraffe_ outfit and will work as expected (
r will store the result of i x f).It is possible to cast any of the above types into any other type, note that casting a
float to int will behave like a Math.floor() as it will remove the values behind the floating point.
Casting a float or a int to bool will return true if the variable is not equal to zero.#### constructor
The variable types are also their own class constructor ; in fact a
float variable can be thought of as an _instance_ of a _Float_ class.This declarations are equally valid:
glslint i = 1;
int i = int( 1 );
int i = int( 1.9995 );
int i = int( true );
This may not sound like much for scalar types, it's not very different from casting, but it will make sense when addressing the overload section.Ok, so these three are the
primitive types, things you can't live without but of course, GLSL has more to offer.Vectors
In Javascript like in GLSL, you'll need more sophisticated ways of handling data, that's where
vectors come in handy.
I suppose that you've already coded a Point class in JavaScript to hold together a x and a y value, the code for this would go like:
glsl// 'class' definition:
var Point = function( x, y ){
this.x = x || 0;
this.y = y || 0;
}//and you would instantiate it like:
var p = new Point( 100,100 );
As we've just seen, this is SO wrong at SO many levels! That
var keyword for one, then the horrendous this, then again untyped x and y values...
No, this is not going to work in shaderland.Instead, GLSL exposes built-in data structures to hold data together, namely:
*
bvec2: a 2D Boolean vector, bvec3: a 3D Boolean vector, bvec4: a 4D Boolean vector
* ivec2: a 2D Integer vector, ivec3: a 3D Integer vector, ivec4: a 4D Integer vector
* vec2: a 2D Float vector, vec3: a 3D Float vector, vec4: a 4D Float vectorYou immediately noticed that there's a type of vector for each primitive type, clever bunny.
From what we just saw, you can deduce that a
bvec2 will hold two values of type bool and a vec4 will hold four float values.Another thing introduced by vectors is a number of dimensions, it doesn't mean that a 2D vector is used when you render 2D graphics and a 3D vector when you do 3D.
What would a 4D vector represent then? (well, actually it is called a tesseract or hypercube)
No, the dimensions represent the number and the type of components or variables stored into the vector:
glsl// let's create a 2D Boolean vector
bvec2 b2 = bvec2 ( true, false );// let's create a 3D Integer vector
ivec3 i3 = ivec3( 0,0,1 );
// let's create a 4D Float vector
vec4 v4 = vec4( 0.0, 1.0, 2.0, 1. );
b2 stores two different boolean values, i3 stores 3 different integer values and v4 stores 4 different float values.but how to retrieve those values?
in the case of
scalars, the answer is obvious ; with float f = 1.2;, the variable f holds the value 1.2.
With vectors it's a bit different and quite beautiful.#### accessors
There are different ways of accessing the values
glsl// let's create a 4D Float vector
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );
to retrieve the 4 values, you can do the following:
glslfloat x = v4.x; // x = 0.0
float y = v4.y; // y = 1.0
float z = v4.z; // z = 2.0
float w = v4.w; // w = 3.0
nice and easy ; but the following are equally valid ways of accessing your data:
glslfloat x = v4.x = v4.r = v4.s = v4[0]; // x = 0.0
float y = v4.y = v4.g = v4.t = v4[1]; // y = 1.0
float z = v4.z = v4.b = v4.p = v4[2]; // z = 2.0
float w = v4.w = v4.a = v4.q = v4[3]; // w = 3.0
And the clever bunny you are already noticed three things:
*
X, Y, Z & W are used in 3D programs to represent 3D vectors
* R, G, B & A are used to encode colors and alpha
* [0], [1], [2] & [3] mean that we have a random access array of valuesSo depending on whether you're manipulating 2D or 3D coordinates, a color with or without an alpha value or simply some random variables, you can pick the most suited vector type and size.
Typically 2D coordinates and vectors (in the geometric sense) are stored as a
vec2, vec3 or vec4, colors as vec3 or vec4 if you need opacity but there is no restriction on how to use the vectors.
For instance, if you want to store only one boolean value in a bvec4, it's possible, it's just a waste of memory.note: in a shader, color values (
R, G, B & A) are normalised, they range from 0 to 1 and not from 0 to 0xFF, so you'd rather use a Float vec4 than an Integer ivec4 to store them.Nice already, but there's more!
#### swizzle
It is possible to return more than one value at once ; say you need only the
X and Y values of a vec4, in JavaScript, you'd have to write something like:
glslvar needles = [0, 1]; // location of 'x' & 'y' in our data structure
var a = [ 0,1,2,3 ]; // our 'vec4' data structure
var b = a.filter( function( val, i, array ) {
return needles.indexOf( array.indexOf( val ) ) != -1;
});
// b = [ 0, 1 ]//or more literally:
var needles = [0, 1];
var a = [ 0,1,2,3 ]; // our 'vec4' data structure
var b = [ a[ needles[ 0 ] ], a[ needles[ 1 ] ] ]; // b = [ 0, 1 ]
Ugly. In GLSL you can retrieve them like so:
glsl// create a 4D Float vector
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );//and retrieve only the X & Y components
vec2 xy = v4.xy; // xy = vec2( 0.0, 1.0 );
What just happened?! when you concatenate accessors, GLSL gracefully returns a subset of the values you asked for, in the best suited vector format.
Indeed, the vector is a random access data structure, like an array in JavaScript if you want.
So not only can you retrieve a subset of your data, but you can also specify the order in which you need it, this will invert the values of the components of a vector:
glsl// create a 4D Float vector: R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );//and retrieve the color components in the A,B,G,R order
vec4 backwards = color.abgr; // backwards = vec4( 1.0, 0.0, 0.8, 0.2 );
And of course, you can ask the same component multiple times:
glsl// create a 4D Float vector: R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );//and retrieve a GAG vec3 based on the G & A channels of the color
vec3 GAG = color.gag; // GAG = vec4( 0.8, 1.0, 0.8 );
This is extremely handy to combine parts of vectors together, extract only the rgb channels of a RGBA color etc.
#### overload everything!
In the types section, I mentioned something about the constructor and that's yet again a great feature of GLSL ; overloading.
For those who don't know, overloading an operator or a function roughly means: _'changing the behaviour of said operator or function depending on the operands/arguments'_.
Overloading is not allowed in JavaScript, so this may be a bit strange at first but I'm sure that once you get used to it, you'll wonder why it is not implemented in JS (short answer, typing).
The most basic example of operator overloading goes as follow:
glslvec2 a = vec2( 1.0, 1.0 );
vec2 b = vec2( 1.0, 1.0 );
//overloaded addition
vec2 c = a + b; // c = vec2( 2.0, 2.0 );
WHAT? So you can add things that are not numbers?!Yes, precisely. Of course this applies to all operators (
+, -, * & /) but that's only the beginning.
Consider the following snippet:
glslvec2 a = vec2( 0.0, 0.0 );
vec2 b = vec2( 1.0, 1.0 );
//overloaded constructor
vec4 c = vec4( a , b ); // c = vec4( 0.0, 0.0, 1.0, 1.0 );
We built a vec4 out of two vec2, by doing so, the new vec4 used the a.x and a.y as the X, Y components of c.
Then it took b.x and b.y and used them as the Z and W components of c.This is what happens when a function is overloaded to accept different arguments, in this case, the
vec4 constructor.
It means that many versions of the same method with a different signature can coexist in the same program, for instance the following declarations are all valid:
glslvec4 a = vec4(1.0, 1.0, 1.0, 1.0);
vec4 a = vec4(1.0);// x, y, z, w all equal 1.0
vec4 a = vec4( v2, float, v4 );// vec4( v2.x, v2.y, float, v4.x );
vec4 a = vec4( v3, float );// vec4( v3.x, v3.y, v3.z, float );
etc.
The only thing you should make sure of is to provide enough arguments to feed your vector.Last thing, you are allowed to overload the built-in functions in your program so they can take arguments they were not designed for (this shouldn't happen too often though).
#### more types
Vectors are fun, they're the meat of your shader.
There are other primitives such as Matrices and Texture samplers which will be covered later in the book.
We can also use Arrays. Of course they have to be typed and there are twists:
* they have a fixed size
* you can't push(), pop(), splice() etc. and there is no
`length` property
* you can't initialize them immediately with values
* you have to set the values individuallythis won't work:
glslint values[3] = [0,0,0];
but this will:
glslint values[3];
values[0] = 0;
values[1] = 0;
values[2] = 0;
This is fine when you know your data or have small arrays of values.
If you want a more expressive way of declaring a variable,
there is also a `struct` type. These are like _objects_ without methods ;
they allow to store and access multiple variables inside the same object
glslstruct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
then you can set and retrieve the values of _colors_ by doing:
glsl//initialize the struct with some values
ColorStruct sandy = ColorStruct( vec3(0.92,0.83,0.60),
vec3(1.,0.94,0.69),
vec3(0.95,0.86,0.69) );//access a values from the struct
sandy.color0 // vec3(0.92,0.83,0.60)
This is syntactic sugar but it can help you write cleaner code, at least code you're more familiar with.#### statements & conditions
Data structures are nice as such but we _might_ need to iterate or perform conditional tests at some point.
Fortunately for us, the syntax is very close to the JavaScript.
A condition is like:
glslif( condition ){
//true
}else{
//false
}
A for loop is usually:
glslconst int count = 10;
for( int i = 0; i <= count; i++){
//do something
}
or with a float iterator:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
//do something
}
Note that `count` will have to be defined as a `constant`.
This means prefixing the type with a `const` qualifier, we'll cover this in a second.we also have the
`break` and `continue` statements:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
if( i < 5. )continue;
if( i >= 8. )break;
}
Note that on some hardware, `break` does not work as expected and the loop doesn't bail out early.In general, you'll want to keep the iteration count as low as possible and avoid the loops and the conditionals as often as you can.
#### qualifiers
On top of the variable types, GLSL uses qualifiers.
Long story short, qualifiers help the compiler know which variable is what.
For instance some data can only be provided by the CPU to the GPU, those are called attributes and uniforms.
The attributes are reserved for the vertex shaders, the uniforms can be used in both the vertex and the fragment shaders.
There's also a
`varying` qualifier used to pass variables between the vertex and the fragment shader.I won't go too much into details here as we're mostly focused on the fragment shader but later in the book, you'll see something like:
glsluniform vec2 u_resolution;
See what we did here? We stuck a `uniform` qualifier before the type of the variable
This means that the resolution of the canvas we're working on is passed to the shader from the CPU.
The width of the canvas is stored in the x and the height in the y component of the 2D vector.When the compiler sees a variable preceded by this qualifier, it will make sure that you can't set those values at runtime.
The same applied to our
`count` variable which was the limit of our `for` loop:
glslconst float count = 10.;
for( ... )
When we use a `const` qualifier, the compiler will make sure that we set the variable's value only once, otherwise it's not a constant.There are 3 extra qualifiers that are used in the functions signatures :
`in`, `out` and `inout`.
In JavaScript, when you pass scalar arguments to a function, their value is read-only and if you change their values inside the function,
the changes are not applied to the variable outside the function.
glslfunction banana( a ){
a += 1;
}
var value = 0;
banana( value );
console.log( value );// > 0 ; the changes are not taken into account outside the function
With arguments qualifiers, you can specify the behaviour of the arguments:
*
`in` will be read-only ( default )
* `out` write-only: you can't read the value of this argument but you can set it
* `inout` read-write: you can both get and set the value of this variableRewriting the banana method in GLSL would look like
glslvoid banana( inout float a ){
a += 1.;
}
float A = 0.;
banana( A ); //now A = 1.;
This is very different from JS and quite powerful too but you don't have to specify the signature qualifiers (the default is read-only).#### space & coordinates
Final note, in the DOM and the Canvas 2D, we're used to have the Y axis pointing 'down'.
This makes sense in the context of a DOM as it follows the way a web page unrolls ; the navbar at the top, content expanding towards the bottom.
In a WebGL canvas, the Y axis is flipped: Y points 'up'.
This means that the origin, the point (0,0), is located at the bottom left corner of a WebGL context, not at the top left corner like in a 2D Canvas.
The textures coordinates follow this rule which might be counter-intuitive at first.
And we're done!
Of course we could have gone deeper into the various concepts but as mentioned earlier, this is meant to give a BIG HUG to the newcomers.
It's a quite a lot to ingest but with patience and practice, this will become more and more natural.I hope you found some of this useful, now what about starting your journey through the book?
---
Appendix/04/README Pl (appendix/04/README-pl.md)
Wprowadzenie dla osób przychodzących z JS
przez Nicolas Barradeau
Jeśli jesteś programistą JavaScript, prawdopodobnie nieco zaskoczy Cię treść tej książki.
Rzeczywiście, istnieje wiele różnic pomiędzy manipulowaniem wysokopoziomowym kodem JS a zagłębianiem się w świat shaderów.
Jednak w przeciwieństwie do niskopoziomowego języka asemblerowego, GLSL jest czytelny dla człowieka i jestem pewien, że po zrozumieniu jego specyfiki szybko zaczniesz go używać.
Zakładam, że masz podstawową (choć może płytką) wiedzę o JavaScript, ale także o Canvas API.
Jeśli nie, nie martw się – większość tej sekcji będzie dla Ciebie zrozumiała.
Nie będę zagłębiać się zbytnio w szczegóły, a niektóre kwestie mogą być _półprawdziwe_; nie oczekuj "wyczerpującego przewodnika", ale raczej
WIELKI UŚCISK
JavaScript świetnie nadaje się do szybkiego prototypowania; wrzucasz garść losowych, nieotypowanych zmiennych i metod, możesz dynamicznie dodawać i usuwać członków klas, odświeżać stronę i sprawdzać, czy wszystko działa,
wprowadzać zmiany, odświeżać stronę, powtarzać – życie jest proste.
Możesz się więc zastanawiać, jaka jest różnica między JavaScriptem a GLSL.
W końcu oba działają w przeglądarce, oba służą do rysowania różnych efektownych rzeczy na ekranie i pod tym względem JS jest łatwiejszy w użyciu.
Główna różnica polega jednak na tym, że JavaScript jest językiem interpretowanym, podczas gdy GLSL jest językiem kompilowanym.
Kompilowany program jest wykonywany natywnie przez system operacyjny, jest niskopoziomowy i zazwyczaj szybki.
Interpretowany program wymaga wirtualnej maszyny (VM) do wykonania, jest wysokopoziomowy i zazwyczaj wolniejszy.
Gdy przeglądarka (czyli _JavaScriptowa VM_) wykonuje lub interpretuje fragment kodu JS, nie wie, jaka zmienna czym jest i jaka funkcja co robi (z wyjątkiem oczywistych przypadków, takich jak TypedArrays).
Dlatego nie może nic zoptymalizować _z góry_, więc potrzebuje trochę czasu, aby przeczytać Twój kod, wywnioskować (dedukować na podstawie użycia) typy Twoich zmiennych i metod,
a kiedy to możliwe, przekształci _część_ Twojego kodu w kod asemblerowy, który wykona się znacznie szybciej.
To powolny, mozolny i niesamowicie skomplikowany proces – jeśli interesują Cię szczegóły, polecam przyjrzeć się działaniu silnika V8 w Chrome.
Najgorsze jest to, że każda przeglądarka optymalizuje JS na swój sposób, a cały proces jest _ukryty_ przed użytkownikiem; jesteś bezsilny.
Kompilowany program nie jest interpretowany; system operacyjny go uruchamia, a jeśli program jest poprawny, zostaje wykonany.
To duża zmiana; jeśli zapomnisz o średniku na końcu linii, Twój kod jest niepoprawny i nie skompiluje się: Twój kod w ogóle nie przekształci się w program.
To surowe, ale tak właśnie działa shader: _skompilowany program wykonywany na GPU_.
Nie bój się! Kompilator, czyli część programu, która dba o to, aby Twój kod był poprawny, stanie się Twoim najlepszym przyjacielem.
Przykłady z tej książki oraz edytor towarzyszący są bardzo przyjazne użytkownikowi.
Powiedzą Ci, gdzie i dlaczego Twój program nie skompilował się, a następnie będziesz musiał wprowadzić poprawki – i za każdym razem, gdy shader będzie gotowy do kompilacji, zostanie on natychmiast wyświetlony.
To świetny sposób na naukę, ponieważ jest bardzo wizualny i tak naprawdę niczego nie możesz zepsuć.
Ostatnia uwaga: shader składa się z dwóch programów, vertex shader i fragment shader.
W skrócie, vertex shader, pierwszy program, otrzymuje geometrię jako dane wejściowe i przekształca ją w serię pikseli (lub fragmentów), które następnie przekazuje do
fragment shader, drugiego programu, który decyduje, jaki kolor nadać pikselom.
Ta książka koncentruje się głównie na tym drugim – we wszystkich przykładach geometria to prosty czworobok pokrywający cały ekran.
Więc! Gotowy?
No to ruszamy!
Silne typowanie
Dla osób przychodzących z JS lub innego języka bez typów, typowanie zmiennych jest obcym konceptem, co sprawia, że typowanie stanowi najtrudniejszy krok w kierunku nauki GLSL.
Typowanie, jak sama nazwa wskazuje, oznacza, że musisz przypisać typ każdej zmiennej (oraz funkcjom, oczywiście).
To zasadniczo oznacza, że słowo
var przestaje istnieć.
Polityka myślenia GLSL wymazała je z powszechnego języka i nie jesteś w stanie go używać, ponieważ... po prostu nie istnieje.Zamiast używać magicznego słowa
var, będziesz musiał _jawnie określić typ każdej używanej zmiennej_, dzięki czemu kompilator będzie widział tylko obiekty i prymitywy, które wie, jak efektywnie obsługiwać.
Minusem braku możliwości użycia słowa var i konieczności _jawnego określania wszystkiego_ jest to, że musisz znać typ wszystkich zmiennych i dobrze je rozumieć.
Spokojnie – jest ich niewiele, a do tego są dość proste (GLSL nie jest frameworkiem Java).Może to brzmieć strasznie, ale ogólnie nie różni się to bardzo od tego, co robisz w JavaScript; jeśli zmienna jest typu
boolean, oczekujesz, że będzie przechowywać true lub false i nic więcej.
Jeśli zmienna jest zadeklarowana jako var uid = XXX;, prawdopodobnie przechowasz w niej wartość całkowitą, a var y = YYY; _może_ odnosić się do wartości zmiennoprzecinkowej.
Co więcej, dzięki silnemu typowaniu nie będziesz tracił czasu na zastanawianie się, czy X == Y (albo czy typeof X == typeof Y? ... albo typeof X !== null && Y...... w każdym razie) – po prostu będziesz tego wiedział, a jeśli nie, kompilator Cię o tym poinformuje.Oto typy skalarne (skalar określa ilość), których możesz używać w GLSL:
bool (Boolean), int (liczba całkowita), float (liczba zmiennoprzecinkowa).
Istnieją też inne typy, ale nie ma co się przejmować – poniższy fragment pokazuje, jak deklarować zmienne (vars) (tak, użyłem zakazanego słowa) w GLSL:
glsl// wartość typu Boolean:
JS: var b = true; GLSL: bool b = true;// wartość typu Integer
JS: var i = 1; GLSL: int i = 1;
// wartość typu Float (liczba)
JS: var f = 3.14159; GLSL: float f = 3.14159;
Nie jest to trudne, prawda? Jak wspomniałem, ułatwia to kodowanie, ponieważ nie tracisz czasu na sprawdzanie typu danej zmiennej.
W razie wątpliwości pamiętaj, że robisz to po to, aby Twój program działał o wiele szybciej niż w JS.#### void
Istnieje typ
void, który w przybliżeniu odpowiada null; jest on używany jako typ zwracany przez funkcję, która nic nie zwraca.
Nie możesz przypisać go do zmiennej.#### boolean
Jak wiesz, wartości logiczne (Boolean) są najczęściej używane w testach warunkowych, np.
if( myBoolean == true ){}else{}.
Choć rozgałęzianie warunkowe jest możliwe na CPU, równoległa natura GLSL czyni to mniej sensownym.
Używanie instrukcji warunkowych jest często wręcz zniechęcane – książka przedstawia kilka alternatywnych technik rozwiązania tego problemu.#### rzutowanie typów
Jak powiedział Boromir: "Nie łączy się po prostu otypowanych prymitywów". W przeciwieństwie do JavaScript, GLSL nie pozwala na wykonywanie operacji pomiędzy zmiennymi o różnych typach.
This for instance:
glslint i = 2;
float f = 3.14159;// próba pomnożenia liczby całkowitej przez wartość zmiennoprzecinkową:
float r = i * f;
nie zadziała, ponieważ próbujesz połączyć _kota_ z _żyrafą_.
Rozwiązaniem jest użycie rzutowania typów; to _sprawi, że kompilator uwierzy_, że i jest typu float, nie zmieniając faktycznie typu i.
glsl// rzutowanie typu zmiennej całkowitej 'i' na float:
float r = float( i ) * f;
Co jest ściśle równoważne przebraniu _kota_ w _żyrafi_ strój i zadziała zgodnie z oczekiwaniami (
r będzie wynikiem i x f).Można rzutować dowolny z wymienionych typów na inny; zauważ, że rzutowanie
float na int działa jak Math.floor(), ponieważ usuwa część dziesiętną.
Rzutowanie float lub int na bool zwróci true, jeśli wartość zmiennej nie wynosi zero.#### konstruktor
Typy zmiennych są również swoimi własnymi konstruktorami klas; w rzeczywistości zmienna typu
float może być traktowana jako _instancja_ klasy _Float_.Następujące deklaracje są równie poprawne:
glslint i = 1;
int i = int( 1 );
int i = int( 1.9995 );
int i = int( true );
Może się to nie wydawać znaczące dla typów skalarnych, ale nie różni się to zbytnio od rzutowania – nabierze sensu, gdy przejdziemy do sekcji dotyczącej przeciążania.Ok, więc te trzy to
typy prymitywne, rzeczy, bez których nie możesz żyć – ale oczywiście GLSL ma do zaoferowania więcej.Wektory
W JavaScript, podobnie jak w GLSL, potrzebujesz bardziej zaawansowanych sposobów obsługi danych, dlatego przydają się wektory.
Przypuszczam, że już napisałeś klasę
Point w JavaScript, która przechowuje razem wartość x i y, kod wyglądałby mniej więcej tak:
glsl// definicja 'klasy':
var Point = function( x, y ){
this.x = x || 0;
this.y = y || 0;
}// i instancjonowałoby się ją tak:
var p = new Point( 100,100 );
Jak właśnie widzieliśmy, to jest TAK niepoprawne na TYLU poziomach! Po pierwsze, użycie słowa kluczowego
var, potem okropne this, a następnie znowu nieotypowane wartości x i y...
Nie, to nie zadziała w świecie shaderów.Zamiast tego GLSL udostępnia wbudowane struktury danych, które służą do przechowywania danych razem, mianowicie:
*
bvec2: 2-wymiarowy wektor Boolean, bvec3: 3-wymiarowy wektor Boolean, bvec4: 4-wymiarowy wektor Boolean
* ivec2: 2-wymiarowy wektor Integer, ivec3: 3-wymiarowy wektor Integer, ivec4: 4-wymiarowy wektor Integer
* vec2: 2-wymiarowy wektor Float, vec3: 3-wymiarowy wektor Float, vec4: 4-wymiarowy wektor FloatNatychmiast zauważyłeś, że dla każdego typu prymitywnego istnieje odpowiedni wektor, spryciarzu.
Z tego, co właśnie widzieliśmy, można wywnioskować, że
bvec2 przechowa dwie wartości typu bool, a vec4 cztery wartości typu float.Inną rzeczą wprowadzoną przez wektory jest liczba wymiarów; nie oznacza to, że do renderowania grafiki 2D używasz 2-wymiarowego wektora, a do 3D – 3-wymiarowego.
Co by wtedy reprezentował 4-wymiarowy wektor? (właściwie nazywa się tesseraktem lub hiperkostką)
Nie, wymiary oznaczają liczbę oraz typ składowych lub zmiennych przechowywanych w wektorze:
glsl// stwórzmy 2-wymiarowy wektor Boolean
bvec2 b2 = bvec2 ( true, false );// stwórzmy 3-wymiarowy wektor Integer
ivec3 i3 = ivec3( 0,0,1 );
// stwórzmy 4-wymiarowy wektor Float
vec4 v4 = vec4( 0.0, 1.0, 2.0, 1. );
b2 przechowuje dwie różne wartości logiczne, i3 przechowuje trzy różne wartości całkowite, a v4 cztery różne wartości zmiennoprzecinkowe.Ale jak odczytać te wartości?
w przypadku
skalarów odpowiedź jest oczywista – przy float f = 1.2; zmienna f przechowuje wartość 1.2.
W przypadku wektorów jest to nieco inne i całkiem piękne.#### akcesory
Istnieją różne sposoby dostępu do wartości
glsl// stwórzmy 4-wymiarowy wektor Float
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );
Aby odczytać te 4 wartości, możesz zrobić następująco:
glslfloat x = v4.x; // x = 0.0
float y = v4.y; // y = 1.0
float z = v4.z; // z = 2.0
float w = v4.w; // w = 3.0
bułka z masłem; ale poniższe sposoby są równie poprawne w dostępie do Twoich danych:
glslfloat x = v4.x = v4.r = v4.s = v4[0]; // x = 0.0
float y = v4.y = v4.g = v4.t = v4[1]; // y = 1.0
float z = v4.z = v4.b = v4.p = v4[2]; // z = 2.0
float w = v4.w = v4.a = v4.q = v4[3]; // w = 3.0
I spryciarzu, już zauważyłeś trzy rzeczy:
*
X, Y, Z i W są używane w programach 3D do reprezentacji wektorów 3D
* R, G, B i A służą do kodowania kolorów oraz kanału alfa
* [0], [1], [2] i [3] oznaczają, że mamy dostęp do wartości w sposób losowy (tablica indeksowana)W zależności od tego, czy manipulujesz współrzędnymi 2D czy 3D, kolorem z lub bez kanału alfa, czy też po prostu różnymi zmiennymi, możesz wybrać najbardziej odpowiedni typ i rozmiar wektora.
Zazwyczaj współrzędne 2D i wektory (w sensie geometrycznym) przechowywane są jako
vec2, vec3 lub vec4, kolory jako vec3 lub vec4 (jeśli potrzebujesz kanału alfa), ale nie ma ograniczeń co do sposobu użycia wektorów.
Na przykład, jeśli chcesz przechować tylko jedną wartość logiczną w bvec4, jest to możliwe, ale to marnotrawstwo pamięci.uwaga: w shaderze wartości kolorów (
R, G, B i A) są normalizowane, mieszczą się w przedziale od 0 do 1, a nie od 0 do 0xFF, dlatego lepiej użyć Float vec4 niż Integer ivec4 do ich przechowywania.Już nieźle, ale to nie wszystko!
#### swizzle
Można zwrócić więcej niż jedną wartość jednocześnie; powiedzmy, że potrzebujesz tylko składowych
X i Y z vec4, w JavaScript musiałbyś napisać coś takiego:
glslvar needles = [0, 1]; // pozycja 'x' i 'y' w naszej strukturze danych
var a = [ 0,1,2,3 ]; // nasza struktura danych 'vec4'
var b = a.filter( function( val, i, array ) {
return needles.indexOf( array.indexOf( val ) ) != -1;
});
// b = [ 0, 1 ]// albo bardziej bezpośrednio:
var needles = [0, 1];
var a = [ 0,1,2,3 ]; // our 'vec4' data structure
var b = [ a[ needles[ 0 ] ], a[ needles[ 1 ] ] ]; // b = [ 0, 1 ]
Brzydko. W GLSL możesz je uzyskać w ten sposób:
glsl// stwrórz 4-wymiarowy wektor Float
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );// i odczytaj tylko 'x' i 'y'
vec2 xy = v4.xy; // xy = vec2( 0.0, 1.0 );
Co tu się stało?! Gdy skonkatenujesz akcesory, GLSL elegancko zwraca podzbiór wartości, o które prosiłeś, w najbardziej odpowiednim formacie wektora.
Rzeczywiście, wektor to struktura danych o losowym dostępie, podobna do tablicy w JavaScript.
Tak więc, nie tylko możesz pobrać podzbiór swoich danych, ale także określić kolejność, w jakiej mają być zwrócone – może to odwrócić kolejność składowych wektora:
glsl// stwórz 4-wymiarowy wektor: R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );// i odczytaj go w kolejności: A,B,G,R
vec4 backwards = color.abgr; // backwards = vec4( 1.0, 0.0, 0.8, 0.2 );
I oczywiście, możesz zapytać o tę samą składową wielokrotnie:
glsl// stwórz 4-wymiarowy wektor: R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );// i odczytaj GAG (Green, Alpha, Green) vec3 z kanałów G i A
vec3 GAG = color.gag; // GAG = vec4( 0.8, 1.0, 0.8 );
Jest to niezwykle przydatne, aby łączyć części wektorów, wyodrębniać tylko kanały rgb z koloru RGBA itp.
#### przeciążaj wszystko!
W sekcji o typach wspomniałem o konstruktorze i tu mamy kolejną świetną cechę GLSL – przeciążanie.
Dla tych, którzy nie wiedzą, przeciążanie operatora lub funkcji oznacza mniej więcej: _"zmianę zachowania danego operatora lub funkcji w zależności od operandów/argumentów"_.
Przeciążanie nie jest dozwolone w JavaScript, więc na początku może to wydawać się dziwne, ale jestem pewien, że gdy się do tego przyzwyczaisz, zastanowisz się, dlaczego nie zostało to zaimplementowane w JS (krótka odpowiedź, typowanie).
Najprostszy przykład przeciążania operatorów wygląda następująco:
glslvec2 a = vec2( 1.0, 1.0 );
vec2 b = vec2( 1.0, 1.0 );
// przeciążone dodawanie
vec2 c = a + b; // c = vec2( 2.0, 2.0 );
CO? Czyli można dodawać rzeczy, które nie są liczbami?!Tak, dokładnie. Oczywiście dotyczy to wszystkich operatorów (
+, -, * oraz /), ale to dopiero początek.
Rozważ poniższy fragment:
glslvec2 a = vec2( 0.0, 0.0 );
vec2 b = vec2( 1.0, 1.0 );
// przeciążony konstruktor
vec4 c = vec4( a , b ); // c = vec4( 0.0, 0.0, 1.0, 1.0 );
Zbudowaliśmy vec4 z dwóch vec2, przy czym nowy vec4 użył a.x i a.y jako składowych X i Y wektora c.
Następnie wziął b.x i b.y i użył ich jako składowych Z i W.Tak właśnie działa przeciążony konstruktor vec4, który akceptuje różne argumenty.
Oznacza to, że wiele wersji tej samej funkcji o różnych sygnaturach może współistnieć w jednym programie, na przykład następujące deklaracje są wszystkie poprawne:
glslvec4 a = vec4(1.0, 1.0, 1.0, 1.0);
vec4 a = vec4(1.0);// x, y, z, w wszystkie są równe 1.0
vec4 a = vec4( v2, float, v4 );// vec4( v2.x, v2.y, float, v4.x );
vec4 a = vec4( v3, float );// vec4( v3.x, v3.y, v3.z, float );
etc.
Jedyne, na co musisz zwrócić uwagę, to dostarczenie wystarczającej liczby argumentów, aby wypełnić Twój wektor.Ostatnia rzecz, możesz przeciążać wbudowane funkcje w swoim programie, aby przyjmowały argumenty, dla których nie zostały zaprojektowane (choć nie powinno się to zdarzać zbyt często).
#### inne typy
Wektory są fajne, to sedno Twojego shadera.
Istnieją inne prymitywy, takie jak macierze (Matrices) i próbki tekstur (Texture samplers), które zostaną omówione później w książce.
Możemy także używać tablic (Arrays). Oczywiście muszą być typowane, a przy tym występują pewne pułapki:
* mają ustalony rozmiar
* nie możesz używać metod push(), pop(), splice() itp., a właściwość
`length` nie istnieje
* nie możesz od razu zainicjalizować ich wartościami
* musisz przypisywać wartości pojedynczoto nie zadziała:
glslint values[3] = [0,0,0];
ale to zadziała:
glslint values[3];
values[0] = 0;
values[1] = 0;
values[2] = 0;
To jest w porządku, gdy znasz swoje dane lub masz małe tablice wartości.
Jeśli chcesz bardziej ekspresyjnego sposobu deklaracji zmiennej,
możesz użyć również typu `struct`. Są one jak _obiekty_ bez metod;
pozwalają przechowywać i uzyskiwać dostęp do wielu zmiennych w jednym obiekcie.
glslstruct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
następnie możesz ustawiać i odczytywać wartości _kolorów_ w następujący sposób:
glsl// zainicjuj struct z jakimiś wartościami
ColorStruct sandy = ColorStruct( vec3(0.92,0.83,0.60),
vec3(1.,0.94,0.69),
vec3(0.95,0.86,0.69) );// odczytaj wartość ze struct
sandy.color0 // vec3(0.92,0.83,0.60)
To lukier składniowy, ale może pomóc w pisaniu bardziej przejrzystego kodu, przynajmniej takiego, do którego jesteś przyzwyczajony.#### instrukcje i warunki
Struktury danych są przydatne, ale może będziesz musiał iterować lub wykonywać testy warunkowe w pewnym momencie.
Na szczęście składnia jest bardzo zbliżona do tej w JavaScript.
Warunek wygląda tak:
glslif( warunek ){
// prawda
}else{
// fałsz
}
Pętla for zazwyczaj wygląda tak:
glslconst int count = 10;
for( int i = 0; i <= count; i++){
// zrób coś
}
lub z iteratorem typu float:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
// zrób coś
}
Zauważ, że count musi być zdefiniowane jako stała.
Oznacza to, że poprzedzasz typ kwalifikatorem const, o czym opowiem za chwilę.Mamy również instrukcje
`break` i `continue`:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
if( i < 5. )continue;
if( i >= 8. )break;
}
Miej na uwadze, że na niektórych urządzeniach instrukcja `break` może nie działać zgodnie z oczekiwaniami i pętla nie przerwie iteracji wcześniej.Ogólnie rzecz biorąc, powinieneś utrzymywać liczbę iteracji na możliwie najniższym poziomie i unikać pętli oraz instrukcji warunkowych tak często, jak to możliwe.
#### kwalifikatory
Oprócz typów zmiennych, GLSL używa kwalifikatorów.
Krótko mówiąc, kwalifikatory pomagają kompilatorowi zrozumieć, jaka jest rola danej zmiennej.
Na przykład, niektóre dane mogą być dostarczane tylko przez CPU do GPU, nazywamy je atrybutami i uniformami.
Atrybuty są zarezerwowane dla vertex shaderów, a uniformy mogą być używane zarówno w vertex, jak i fragment shaderach.
Jest też kwalifikator
`varying`, służący do przekazywania zmiennych między vertex a fragment shaderem.Nie będę zagłębiać się tutaj w szczegóły, ponieważ skupiamy się głównie na fragment shaderze, ale później w książce zobaczysz coś takiego:
glsluniform vec2 u_resolution;
Widzisz, co zrobiliśmy? Dodaliśmy kwalifikator `uniform` przed typem zmiennej.
Oznacza to, że rozdzielczość kanwy, nad którą pracujemy, jest przekazywana do shadera z CPU.
Szerokość kanwy zapisana jest w komponencie x, a wysokość w komponencie y 2-wymiarowego wektora.Gdy kompilator napotka zmienną poprzedzoną tym kwalifikatorem, upewni się, że nie możesz zmieniać tych wartości w czasie wykonywania programu.
To samo dotyczy naszej zmiennej
`count`, która była limitem w pętli `for`:
glslconst float count = 10.;
for( ... )
Kiedy używamy kwalifikatora `const`, kompilator upewni się, że wartość zmiennej zostanie ustawiona tylko raz, w przeciwnym razie nie jest to stała.Istnieją trzy dodatkowe kwalifikatory używane w sygnaturach funkcji: in, out oraz inout.
W JavaScript, gdy przekazujesz prymitywne argumenty do funkcji, ich wartość jest tylko do odczytu, a jeśli zmienisz ich wartość wewnątrz funkcji,
zmiany nie mają wpływu na zmienną poza funkcją.
glslfunction banana( a ){
a += 1;
}
var value = 0;
banana( value );
console.log( value );// > 0 ; zmiany nie są brane pod uwagę poza funkcją
With arguments qualifiers, you can specify the behaviour of the arguments:
*
`in` will be read-only ( default )
* `out` write-only: you can't read the value of this argument but you can set it
* `inout` read-write: you can both get and set the value of this variablePrzepisanie funkcji banana do GLSL wyglądałoby tak:
glslvoid banana( inout float a ){
a += 1.;
}
float A = 0.;
banana( A ); // teraz A = 1.;
To bardzo różni się od JS i jest również potężne, ale nie musisz jawnie określać kwalifikatorów w sygnaturze (domyślnie są one tylko do odczytu).#### przestrzeń i współrzędne
Ostatnia uwaga: w DOM oraz w 2D Canvas jesteśmy przyzwyczajeni, że oś Y wskazuje w dół.
Ma to sens w kontekście DOM, ponieważ odpowiada sposobowi, w jaki rozwija się strona internetowa – pasek nawigacyjny na górze, zawartość rozciągająca się ku dołowi.
W kanwie WebGL oś Y jest odwrócona: Y wskazuje w górę.
Oznacza to, że punkt początkowy, czyli (0,0), znajduje się w lewym dolnym rogu kontekstu WebGL, a nie w lewym górnym, jak ma to miejsce w 2D Canvas.
Współrzędne tekstur podlegają tej zasadzie, co na początku może być nieintuicyjne.
I to wszystko!
Oczywiście moglibyśmy zagłębić się w różne koncepcje, ale jak wspomniano wcześniej, chodziło o to, aby dać WIELKI UŚCISK nowoprzybyłym.
To sporo materiału do przyswojenia, ale z cierpliwością i praktyką stanie się to coraz bardziej naturalne.Mam nadzieję, że część z tego okaże się przydatna. A teraz, co powiesz na rozpoczęcie swojej podróży przez tę książkę?
---
Appendix/04/README Ru (appendix/04/README-ru.md)
Введение для JavaScript-программистов
автор Николя Баррадо
Если вы JavaScript-разработчик, велика вероятность, что вы будете немного озадаченЫ, читая эту книгу. В самом деле, есть множество различий между манипулированием высокоуровневыми абстракциями на JS и ковырянием в шейдерах. Но, в отличие от лежащего на более низком уровне языка ассемблера, GLSL является человекочитаемым, и я уверен, что разобравшись с его особенностями, вы быстро сможете начать его использовать.
Я предполагаю, что у вас есть хотя бы поверхностные знания JavaScript и Canvas API. Если это не так - ничего страшного. Вам всё равно будет интересно читать большую часть этой главы.
Так же, я не буду сильно углубляться в детали, и некоторые вещи могут быть лишь _полуправдой_. Эта глава не является подробным руководством.
JavaScript очень хорош для быстрого прототипирования. Вы можете беспорядочно набросать кучу нетипизированных переменных и методов, динамически добавлять и удалять члены класса, обновить страницу и увидеть как она работает. Затем сделать изменения в соответствии с увиденным, обновить страницу, повторить. Жизнь - простая штука. Так в чём же разница между JavaScript и GLSL? Они оба работают в браузере, оба используются для рисования всяких прикольных штук на экране, и к тому же, JS проще в использовании.
ОСновная разница в том, что JavaScript - интерпретируемый язык, в то время как GLSL - компилируемый. Скомпилированная программа исполняется нативно, она является низкоуровневой и в целом высокопроизводительна. Интерпретируемая программа требует виртуальную машину для своего исполнения, является высокоуровневой и в общем случае более медленной.
Когда браузер (_виртуальная машина JavaScript_) исполняет или интерпретирует кусок кода на JS, он не имеет ни малейшего понятия чем является каждая переменная и что делает каждая функция (за исключением типизированных массивов). Поэтому он не может оптимизировать что-либо _наперёд_. Чтение кода браузером занимает какое-то время, чтобы вывести (исходя из использования) типы переменных и методов, и, по возможности, преобразовать _часть_ кода в ассемблер, который будет исполняться намного быстрее.
Это медленный, болезненный и до сумасшествия сложный процесс. Если вам интересны подробности, рекомендую посмотреть как работает движок V8 в Хроме. Хуже всего то, что браузер оптимизирует JS как ему хочется, и этот процесс _скрыт_ от программиста. Вы бессильны.
Компилируемая программа не интерпретируется на ходу. Её запускает операционная система, и она исполняется, если она корректна. Это многое меняет. Если вы забудете точку с запятой в конце строки, ваш код станет некорректным и просто не скомпилируется. Он вообще не превратится в программу.
Это сурово, но это то, чем является шейдер: _компилируемая программа для исполнения на GPU_. Не пугайтесь! Компилятор, то есть та программа которая проверяет ваш код на корректность, станет вашим лучшим другом. Примеры и редактор в этой книге очень дружественны к пользователю. Она подскажут в каком месте программа не скомпилировалась, и когда после всех правок шейдер будет готов к компиляции, результат его работы будет немедленно отображён. Это отличный способ обучения в силу его наглядности и невозможности что-либо сломать.
И последнее: шейдер состоит из двух программ: вершинного и фрагментного шейдера. Вкратце, вершинный шейдер (первая программа) принимает на вход и преобразовывет геометрию, которая затем превращается в последовательность пикселей (или фрагментов), поступающих на вход второго шейдера. И уже второй шейдер решает в какой цвет нужно покрасить пиксели. Эта книга посвящена именно вторым шейдерам. Во всех примерах геометрия - это прямоугольник, покрывающий всю доступную область.
Готовы?
Поехали!
Сильная типизация
Когда вы приходите с JS или любого другого нетипизированного языка, типизирование переменных является для вас чужеродной концепцией, и это станет сложнейшим шагом при переходе к GLSL. Типизация, как легко догадаться, означает, что вам придётся давать тип каждой переменной и функции. Отсюда следует, что ключевого слова
var больше не существует. Считайте, что полиция мыслей от GLSL стёрла его из общеупотребимого языка и вы больше не можете его произносить потому что, ну... его не существует.Вместо использования волшебного слова
var вам придётся _явно указывать тип каждой переменной_, тогда компилятор увидит те объекты и примитивы, с которыми он умеет эффективно обращаться. Обратная сторона невозможности использования ключевого слова var заключается в том, что вам нужно очень хорошо знать особенности типов всех переменных. Но поверьте, типов в GLSL немного, и они все достаточно просты (GLSL - не Java-фреймворк).Всё это может выглядеть пугающе, но всё же это не сильно отличается от того, что вы обычно делаете на JS. Например, если переменная булева, то в ней может храниться только
true или false. Если переменная называется var uid = XXX;, то в ней вероятно хранится целочисленное значение. Если же она объявлена как var y = YYY;, то это _возможно_ ссылка на значение с плавающей точкой. Что ещё лучше, при использовании сильных типов вам не придётся гадать что означает X == Y, и означает ли это typeof X == typeof Y, или typeof X !== null && Y.... В любом случае, вы знаете что здесь написано, а если и не знаете, то компилятор знает точно.Перечислим скалярные типы языка GLSL (скаляр описывает количество):
bool (булев тип), int (целочисленный) и float (значения с плавающей точкой). Есть и другие типы, но пока давайте рассмотрим как объявляются переменные в GLSL:glsl// булево значение
JS: var b = true; GLSL: bool b = true;// целое значение
JS: var i = 1; GLSL: int i = 1;
// число с плавающей точкой
JS: var f = 3.14159; GLSL: float f = 3.14159;
Не очень трудно, правда? Как было замечено выше, такой подход делает программирование проще, так как вы не тратите время на выслеживание типа какой-либо переменной. Всё ещё сомневаетесь? Помните, что это так же делается для того, чтобы ваша программа исполнялась в разы быстрее, чем на JS.#### void
В GLSL есть тип
void, который приблизительно соответствует null. Он используется в качестве возвращаемого типа для метода, который не возвращает ничего, и вы не можете объявить переменную этого типа.#### boolean
Как вам известно, булевы значения в основном используются для проверки условий:
if( myBoolean == true ){}else{}. Условное ветвление очень легко использовать на CPU, но параллельная природа GLSL делает это утверждение не совсем верным. Как правило, использование условных переходов не рекомендуется, и в книге описано несколько способов обойти это ограничение.#### приведение типов
Как говорил Боромир, нельзя просто так взять и смешать типизированные примитивы. В отличие от JavaScript, GLSL не позволит вам выполнять операции между переменными различных типов.
Например вот это:
glslint i = 2;
float f = 3.14159;// попытка умножить целое на значение с плавающей точкой
float r = i * f;
не будет работать, потому что вы пытаетесь скрестить _кошку_ с _жирафом_. Проблема решается с помощью приведения типов, которое _заставит компилятор поверить_, что i имеет тип float, не меняя фактический тип i.
glsl//приведение типа целочисленной переменной 'i' к float
float r = float( i ) * f;
Это в точности как переодевание _кошки_ в шкуру _жирафа_, которое будет работать как и ожидается: в
r сохранится результат умножения i x f.Любой из упомянутых выше типов можно привести к любому другому. При этом приведение
float к int будет работать как Math.floor(), удаляя числа справа от запятой. Приведение float или int к булеву типу вернёт true если переменная не равна нулю.#### конструктор
Типы переменных так же являются конструкторами классов для самих себя. Фактически, переменную типа
float можно представлять как _экземпляр_ класса _float_.Следующие объявления равнозначны:
glslint i = 1;
int i = int( 1 );
int i = int( 1.9995 );
int i = int( true );
Для скалярных типов это выглядит весьма тривиально, не особо отличаясь от приведения, но в этом появится больше смысла когда мы дойдём до раздела о перегрузках.Итак, мы изучили три
примитивных типа, без которых невозможно обойтись, но в GLSL есть и другие.Векторы
Как и JavaScript, в GLSL вам понадобятся более продвинутые способы для манипуляции данными, и здесь
векторы будт очень кстати. Я предполагаю, что вам доводилось писать на JS класс Point, который содержит значения x и y, и выглядит как-то так:
glsl// определение:
var Point = function( x, y ){
this.x = x || 0;
this.y = y || 0;
}// объявление экземпляра:
var p = new Point( 100,100 );
Как мы только что видели, этот код жутко неправилен на всех уровнях. Во-первых, это ключевое слово
var, затем это ужасающее this и нетипизированные значения x и y... Нет, такое явно не будет работать в мире шейдеров.Вместо этого GLSL предоставляет встроенные структуры для группировки данных:
*
bvec2: 2D булев вектор, bvec3: 3D булев вектор, bvec4: 4D булев вектор
* ivec2: 2D целочисленный вектор, ivec3: 3D целочисленный вектор, ivec4: 4D целочисленный вектор
* vec2: 2D вектор с плавающей точкой, vec3: 3Dвектор с плавающей точкой, vec4: 4D вектор с плавающей точкойВдумчивый читатель заметит, что каждому примитивному типу соответствует векторный тип. Из написанного выше легко вывести, что
bvec2 содержит два булевых значения, а vec4 будет содержать четыре значения в плавающей точкой.Так же векторы вводят такую величину, как размерность. Это не означает, что вы должны использовать 2D-вектор при отрисовке 2D-графики и 3D при рисовании 3D-изображений. Для чего в таком случае используется четырёхмерный вектор? (ну, на самом деле это называется «тессеракт» или «гиперкуб»)
Нет, размерность указывает на количество компонентов или переменных, хранимых в векторе:
glsl// объявляем двумерный булев вектор
bvec2 b2 = bvec2 ( true, false );// объявляем трёхмерный целочисленный вектор
ivec3 i3 = ivec3( 0,0,1 );
// объявляем четырёхмерный вектор значений с плавающей запятой
vec4 v4 = vec4( 0.0, 1.0, 2.0, 1. );
b2 содержит два различных булевых значения, i3 содержит 3 различных целых, а v4 содержит 4 различных значения с плавающей точкой.Но как обратиться к этим значениям?
В случае скаляров ответ очевиден: при объявлении
float f = 1.2; переменная f содержит значение 1.2. Для векторов всё немного по-другому и выглядит это довольно красиво.#### доступ к элементам векторов
Есть несколько способов доступа к значениям
glsl// объявим четырёхмерный вектор значений с плавающей точкой
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );
четыре его значения можно извлечь следующим образом
glslfloat x = v4.x; // x = 0.0
float y = v4.y; // y = 1.0
float z = v4.z; // z = 2.0
float w = v4.w; // w = 3.0
легко и просто. Ниже приведены равнозначные способы доступа к данным:
glslfloat x = v4.x = v4.r = v4.s = v4[0]; // x = 0.0
float y = v4.y = v4.g = v4.t = v4[1]; // y = 1.0
float z = v4.z = v4.b = v4.p = v4[2]; // z = 2.0
float w = v4.w = v4.a = v4.q = v4[3]; // w = 3.0
Вдумчивый читатель заметил три факта:
*
X, Y, Z и W как правило используются в программах для представления векторов в пространстве
* R, G, B и A используются для кодирования цвета и альфа-канала
* [0], [1], [2] и [3] означают, что векторы являются массивами с произвольным доступомВ зависимости от того, работаете ли вы с двух- или трёхмерными координатами, цветом с альфа-каналом или без такового, или просто какими-то произвольными значениями, вы можете выбрать наиболее подходящий тип и размерность вектора. Обычно координаты и векторы (в геометрическом смысле слова) хранятся как
vec2, vec3 или vec4, цвета как vec3 или vec4, но в целом никаких ограничений на использование переменных нет. Например, никто не запрещает вам хранить единственное булево значение как bvec4, но это приведёт в излишнему расходу памяти.Заметим, что в шейдерах значения цвета (
R, G, B, A) нормализованы, то есть лежат в диапазоне от 0 до 1, а не от 0 до 0xFF, поэтому для них лучше использовать вещественный тип vec4, а не целочисленный ivec4.Уже лучше, но мы идём далее!
#### перемешивание
Из вектора можно извлечь несколько значений одновременно. Например, если вам нужны только
X и Y из vec4, на JavaScript вы бы написали что-то вроде этого:
glslvar needles = [0, 1]; // размещение 'x' и 'y' в структуре данных
var a = [ 0,1,2,3 ]; // структура данных 'vec4'
var b = a.filter( function( val, i, array ) {
return needles.indexOf( array.indexOf( val ) ) != -1;
});
// b = [ 0, 1 ]// или более буквально:
var needles = [0, 1];
var a = [ 0,1,2,3 ]; // структура 'vec4'
var b = [ a[ needles[ 0 ] ], a[ needles[ 1 ] ] ]; // b = [ 0, 1 ]
Выглядит уродливо. В GLSL данные можно извлечь вот так:
glsl// создаём четырёхмерный вектор с плавающей запятой
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );// и извлекаем только X и Y
vec2 xy = v4.xy; // xy = vec2( 0.0, 1.0 );
Что это было?! Когда вы составляете воедино методы доступа к полям, GLSL изящно возвращает запрошенное подмножество в виде значения наиболее подходящего типа. Это возможно, потому что вектор является структурой данных с произвольным доступом, прямо как массив в javaScript. Поэтому, можно не только обратиться к подмножеству данных вектора, но и указать порядок, в котором нужно обращаться. Следующий код обратит порядок компонентов вектора:
glsl// создаём четырёхкомпонентный вектор R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );// и извлекаем компоненты цвета в порядке A,B,G,R
vec4 backwards = v4.abgr; // backwards = vec4( 1.0, 0.0, 0.8, 0.2 );
И конечно же, к одной компоненте можно обратиться многократно:
glsl// создаём четырёхкомпонентный вектор R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );// и извлекаем vec3 с компонентами GAG на основе каналов G и A исходного цвета
vec3 GAG = v4.gag; // GAG = vec4( 0.8, 1.0, 0.8 );
Очень удобно составлять части вектора воедино, извлекать только rgb-компоненты из вектора цвета с прозрачностью и т.п.
#### перегрузим всё!
В разделе о типах я упоминал упоминал конструкторы и ещё одно великолепное свойство языка GLSL - перегрузку. Перегрузка оператора или функции означает _изменение поведения этого оператора или функции в зависимости от операндов/аргументов_. В JavsScript нет перегрузки, поэтому вначале она может показаться вам странной, но немного попользовавшись ей, вы зададитесь вопросом, почему же она не реализована в JavaScript (краткий ответ - типизация).
Рассмотрим простейший пример перегрузки:
glslvec2 a = vec2( 1.0, 1.0 );
vec2 b = vec2( 1.0, 1.0 );
// перегруженное сложение
vec2 c = a + b; // c = vec2( 2.0, 2.0 );
ШТОА? Можно складывать сущности, не являющиеся числами?!Именно. И конечно же, это применимо ко всем операторам (
+, -, * и /), и это только начало.
Рассмотрим фрагмент кода:
glslvec2 a = vec2( 0.0, 0.0 );
vec2 b = vec2( 1.0, 1.0 );
// перегруженный конструктор
vec4 c = vec4( a , b ); // c = vec4( 0.0, 0.0, 1.0, 1.0 );
Мы соорудили vec4 из двух vec2, используя a.x и a.y в качестве компонент X и Y для нового вектора c. Затем мы взяли b.x и b.y в качестве Z и W для c.Так работает перегрузка функции по набору параметров, в данном случае это конструктор
vec4. Это означает, что несколько версий одного и того же метода с различными наборами параметров могут мирно сосуществовать в одной программе. Например, все следующие объявления корректны:
glslvec4 a = vec4(1.0, 1.0, 1.0, 1.0);
vec4 a = vec4(1.0);// x, y, z, w all equal 1.0
vec4 a = vec4( v2, float, v4 );// vec4( v2.x, v2.y, float, v4.x );
vec4 a = vec4( v3, float );// vec4( v3.x, v3.y, v3.z, float );
etc.
От вас требуется только подать достаточное количество параметров для заполнения вектора.Наконец, вы можете перегружать встроенные функции для тех типов аргументов, для которых они не были изначально задуманы (но лучше не делать этого слишком часто).
#### нужно больше типов
Векторы прикольные. Они - мышцы вашего шейдера. Но есть и другие типы, например матрицы и текстурные семплеры, о которых будет рассказано ниже.
В GLSL есть массивы. Конечно же, они типизированные, и у них есть несколько отличий от массивов в JS:
* у них фиксированный размер
* вы не можете использовать push(), pop(), splice() и т.п., свойство
`length` тоже отсутствует
* их нельзя инициализировать значениями при объявлении
* значения нужно задавать по одномувот это работать не будет:
glslint values[3] = [0,0,0];
а вот это заработает:
glslint values[3];
values[0] = 0;
values[1] = 0;
values[2] = 0;
Этого хватает, если вы знаете все ваши данные или работаете с небольшими массивами данных. Если вам нужно больше выразительности, вы можете использовать структуры (`struct`). Они похожи на _объекты_ без методов. Они позволяют хранить несколько переменных в одном объекте:
glslstruct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
например, вы можете задавать и извлекать значения _цвета_ следующим образом:
glsl// инициализируем структуру
ColorStruct sandy = ColorStruct( vec3(0.92,0.83,0.60),
vec3(1.,0.94,0.69),
vec3(0.95,0.86,0.69) );// получем доступ к значениям
sandy.color0 // vec3(0.92,0.83,0.60)
Это синтаксический сахар, но он может помочь вам писать более чистый, или как минимум более привычный код.#### выражения и условия
Структуры данных очень полезны, но рано или поздно нам _возможно_ понадобится проходить по массиву или выполнять проверку условия. К счастью, синтаксис для этого очень близок к JavaScript.
Условие выглядит так:
glslif( condition ){
//true
}else{
//false
}
Цикл for выглядит так:
glslconst int count = 10;
for( int i = 0; i <= count; i++){
//do something
}
пример переменной цикла с плавающей точкой:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
//do something
}
Заметим, что `count` должна быть объявлена константой. Это означает, что перед её объявлением должен быть квалификатор `const`, который будет рассмотрен чуть ниже.Так же нам доступны ключевые слова
`break` и `continue`:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
if( i < 5. )continue;
if( i >= 8. )break;
}
Имейте ввиду, что на некоторых типах оборудования `break` не работает ожидаемым образом и не прерывает цикл заранее.В целом, старайтесь делать количество итераций как можно меньше, и избегайте циклов и ветвлений как можно чаще.
#### квалификаторы
Помимо типов переменных в GLSL есть квалификаторы. Вкратце, квалификаторы сообщают компилятору какая переменная для чего предназначена. Например, некоторые данные для GPU могут приходить только со стороны CPU. Такие данные называются атрибутами и юниформами. Атрибуты встречаются только в вершинных шейдерах, а юниформы - и в вершинных, и во фрагментных. Так же есть квалификатор
`varying`, используемый для передачи переменных от вершинного шейдера к фрагментному.Я не буду сильно углубляться в подробности, ибо мы в основном рассматриваем фрагментные шейдеры*, но далее в книге вам возможно встретится что-то вроде
glsluniform vec2 u_resolution;
Что здесь происходит? Мы задали квалификатор `uniform` перед типом переменной, указав, что разрешение изображения передаётся в шейдер из CPU. Ширина изображения находится в x-компоненте 2D-вектора, а высота - в y-компоненте.Когда компилятор видит переменную, объявленную с этим квалификатором, он сделает чтобы вы не могли записать это значение в рантайме.
То же самое применимо к переменной
`count`, которая была пороговым значением в цикле `for`:
glslconst float count = 10.;
for( ... )
Когда мы используем квалификатор `const`, компилятор не даёт нам перезаписать значение, которое в противном случае не было бы константой.Ещё три квалификатора используются в сигнатурах функций:
`in`, `out` и `inout`. В JavaScript переданные в функцию аргументы предназначены только для чтения. Их изменение внутри функции не приводит к изменению значений за её пределами.
glslfunction banana( a ){
a += 1;
}
var value = 0;
banana( value );
console.log( value );// > 0 ; значение за пределами функции не изменилось
Используя квалификаторы аргументов, можно изменять их поведение:
*
`in` предназначен только для чтения (по умолчанию)
* `out` только для записи: значение такого аргумента нельзя прочитать, но можно записать
* `inout` чтение и записьПерепишем упомянутый выше метод на GLSL:
glslvoid banana( inout float a ){
a += 1.;
}
float A = 0.;
banana( A ); // теперь A = 1.;
Это поведение сильно отличается от JS и даёт множество возможностей. При этом, не обязательно всегда указывать квалификаторы аргументов. По умолчанию аргументы предназначены только для чтения.#### пространство и координаты
Напоследок заметим, что в DOM и Canvas 2D ось Y направлена вниз. Это имеет смысл в контексте DOM, ибо соответствует тому, как свёрстана web-страница: навигационная панель наверху, а контент прокручивается вниз. В webgl-элементе ось Y перевёрнута и указывает вверх.
Это означает, что начало координат (точка (0,0)) расположено в левом нижнем, а не в левом верхнем углу контекста. Текстурные координаты так же следуют этому правилу, которое на первый взгляд кажется контринтуитивным.
На этом всё!
Конечно, мы могли мы углубиться во всяческие детали, но, как было сказано вначале, эта статья писалась как простое введение для новичков. Здесь уже написано достаточно, чтобы переваривать это некоторое время, но с терпением и практикой этот язык будет становиться всё более естественным для вас.Надеюсь, этот текст был полезен, а потому самое время приступить к основному содержимому книги!
---
Appendix/04/README Tr (appendix/04/README-tr.md)
JS'den gelenler için bir giriş
yazan Nicolas Barradeau
Eğer bir JavaScript geliştiricisiyseniz, bu kitabı okurken biraz şaşıracaksınız.
Aslında, üst düzey JS'yi manipüle etmek ile shader'larla uğraşmak arasında birçok fark var.
Yine de, altta yatan assembly diline kıyasla GLSL insan tarafından okunabilir ve eminim ki özelliklerini kabul ettikten sonra kısa sürede çalışmaya başlayacaksınız.
JavaScript hakkında önceden (yüzeysel de olsa) bir bilginiz olduğunu varsayıyorum, ama aynı zamanda Canvas API hakkında da.
Değilse, endişelenmeyin, bu bölümün çoğunu yine de anlayabileceksiniz.
Ayrıca çok fazla detaya girmeyeceğim ve bazı şeyler _yarı doğru_ olabilir, "kesin bir kılavuz" beklemeyin, daha çok
BÜYÜK BİR KUCAKLAMA
JavaScript hızlı prototipleme için harikadır; bir yığın rastgele, tipsiz değişken ve metot fırlatırsınız, dinamik olarak sınıf üyelerini ekleyebilir ve kaldırabilirsiniz, sayfayı yenilersiniz ve çalışıp çalışmadığını görürsünüz,
buna göre değişiklikler yaparsınız, sayfayı yenilersiniz, tekrarlarsınız, hayat kolaydır.
Bu yüzden JavaScript ile GLSL arasındaki farkın ne olduğunu merak edebilirsiniz.
Sonuçta, ikisi de tarayıcıda çalışır, ikisi de ekranda bir sürü eğlenceli şey çizmek için kullanılır ve bu açıdan JS kullanımı daha kolaydır.
Asıl fark, JavaScript'in yorumlanan bir dil olması, GLSL'nin ise derlenen bir dil olmasıdır.
Derlenen bir program işletim sisteminde yerel olarak çalışır, düşük seviyelidir ve genellikle hızlıdır.
Yorumlanan bir program çalıştırılmak için bir Sanal Makine (VM) gerektirir, yüksek seviyelidir ve genellikle yavaştır.
Bir tarayıcı (_JavaScript VM'si_) bir JS parçasını çalıştırdığında veya yorumladığında, hangi değişkenin ne olduğu ve hangi fonksiyonun ne yaptığı hakkında hiçbir fikri yoktur (dikkate değer istisna TypedArrays).
Bu nedenle _önceden_ hiçbir şeyi optimize edemez, bu yüzden kodunuzu okumak, değişkenlerinizin ve metotlarınızın türlerini çıkarmak (kullanımdan türetmek) için biraz zaman alır
ve mümkün olduğunda, kodunuzun _bir kısmını_ çok daha hızlı çalışacak assembly koduna dönüştürür.
Yavaş, zahmetli ve çılgınca karmaşık bir süreçtir, detaylarla ilgileniyorsanız, Chrome'un V8 motorunun nasıl çalıştığını izlemenizi öneririm.
En kötüsü, her tarayıcının JS'yi kendi yöntemiyle optimize etmesidir ve süreç sizden _gizlenir_; çaresizsiniz.
Derlenen bir program yorumlanmaz; işletim sistemi onu çalıştırır, program geçerliyse çalışır.
Bu büyük bir değişiklik; satır sonundaki noktalı virgülü unutursanız, kodunuz geçersizdir, derlenmez: kodunuz hiçbir şekilde programa dönüşmez.
Bu soğuktur ama bir shader budur: _GPU üzerinde çalıştırılan derlenmiş bir program_.
Korkmayın! Kodunuzun geçerli olduğundan emin olan bir derleyici en iyi arkadaşınız olacak.
Bu kitabın örnekleri ve eşlikçi editör çok kullanıcı dostudur.
Programınızın nerede ve neden derlenemediğini söylerler, ardından düzeltmeniz gerekir ve shader derlenmeye hazır olduğunda anında görüntülenir.
Bu, çok görsel olduğu ve gerçekten hiçbir şeyi bozamayacağınız için öğrenmenin harika bir yoludur.
Son not, bir shader 2 programdan oluşur: vertex shader ve fragment shader.
Kısaca, vertex shader, birinci program, girdi olarak bir geometri alır ve onu piksel (veya fragment) serilerine dönüştürür, ardından bunları
fragment shader'a, ikinci programa teslim eder, bu da pikselleri hangi renge boyayacağına karar verir.
Bu kitap ağırlıklı olarak ikincisine odaklanır, tüm örneklerde geometri tüm ekranı kaplayan basit bir dörtgendir.
PEKALA! hazır mısınız?
başlayalım!
Güçlü tipler
JS veya herhangi bir tipsiz dilden geliyorsanız, değişkenlerinizi tiplemek yabancı bir kavramdır, bu da tiplemeyi GLSL'ye doğru atılacak en zor adım yapar.
Tipleme, adından da anlaşılacağı gibi, değişkenlerinize (ve tabii ki fonksiyonlarınıza) bir tip vereceğiniz anlamına gelir.
Bu temelde
var kelimesinin artık var olmadığı anlamına gelir.
GLSL düşünce polisi onu ortak dilden sildi ve siz onu söyleyemezsiniz çünkü, şey... var olmuyor.Sihirli
var kelimesini kullanmak yerine, _kullandığınız her değişkenin tipini açıkça belirtmeniz_ gerekecek, sonra derleyici yalnızca nasıl verimli bir şekilde ele alacağını bildiği nesneleri ve temel türleri görecektir.
var anahtar kelimesini kullanamadığınızda ve _her şeyi belirtmeniz_ gerektiğinde dezavantaj, tüm değişkenlerin tipini bilmeniz ve onları iyi bilmeniz gerektiğidir.
Emin olun, sayıları az ve oldukça basitler (GLSL bir Java framework'ü değildir).Kulağa korkutucu gelebilir ama sonuçta, JavaScript kodlarken yaptığınızdan çok farklı değil; bir değişken
boolean ise, true veya false saklamasını beklersiniz ve başka bir şey değil.
Bir değişken var uid = XXX; olarak adlandırılmışsa, büyük olasılıkla içinde bir tam sayı değeri saklayacaksınız ve var y = YYY; bir kayan noktalı değere referans _olabilir_.
Daha da iyisi, güçlü tipler ile X == Y mi (yoksa typeof X == typeof Y mi? .. yoksa typeof X !== null && Y... ... neyse) diye zaman kaybetmezsiniz; sadece bilirsiniz ve bilmezseniz, derleyici bilir.İşte GLSL'de kullanabileceğiniz skaler tipler (bir skaler bir miktarı tanımlar):
bool (Boolean), int(Tam sayı), float(kayan noktalı Sayı).
Başka tipler de var ama sakin olalım, aşağıdaki snippet GLSL'de var'ların (evet, yasak kelimeyi söyledim) nasıl tanımlanacağını gösterir:
glsl//bir Boolean değeri:
JS: var b = true; GLSL: bool b = true;//bir Tam sayı değeri
JS: var i = 1; GLSL: int i = 1;
//bir Float değeri (bir Sayı)
JS: var f = 3.14159; GLSL: float f = 3.14159;
Çok zor değil, değil mi? yukarıda belirtildiği gibi, verilen bir değişkenin tipini kontrol etmekle zaman kaybetmediğiniz için işleri kolaylaştırır bile.
Şüpheye düştüğünüzde, bunu programınızın JS'den çok daha hızlı çalışması için yaptığınızı hatırlayın.#### void
Kabaca
null'a karşılık gelen bir void tipi vardır, hiçbir şey döndürmeyen bir metodun dönüş tipi olarak kullanılır.
Bir değişkene atayamazsınız.#### boolean
Bildiğiniz gibi, Boolean'lar çoğunlukla koşullu testlerde kullanılır;
if( myBoolean == true ){}else{}.
Koşullu dallanma CPU'da geçerli bir seçenek olsa da, GLSL'nin paralel doğası bunu daha az doğru kılar.
Koşullu ifadelerin kullanımı çoğu zaman önerilmez bile, kitap bunu çözmek için birkaç alternatif teknik açıklar.#### tip dönüştürme
Boromir'in dediği gibi, "İnsan Tiplenmiş temel türleri kolayca birleştiremez". JavaScript'in aksine, GLSL farklı tipteki değişkenler arasında işlem yapmanıza izin vermez.
Örneğin:
glslint i = 2;
float f = 3.14159;//bir tam sayıyı bir float değerle çarpmaya çalışmak
float r = i * f;
düzgün çalışmayacaktır çünkü bir _kedi_ ile bir _zürafayı_ çaprazlamaya çalışıyorsunuz.
Bunun çözümü tip dönüştürme kullanmaktır; bu, i'nin aslında tipini değiştirmeden _derleyicinin i'nin float tipinde olduğuna inanmasını sağlar_.
glsl//'i' tam sayı değişkeninin tipini float'a dönüştürme
float r = float( i ) * f;
Bu, bir _kediyi_ _zürafa kostümüne_ sokmakla kesinlikle eşdeğerdir ve beklendiği gibi çalışacaktır (
r, i x f'nin sonucunu saklayacaktır).Yukarıdaki tiplerin herhangi birini başka herhangi bir tipe dönüştürmek mümkündür,
float'ı int'e dönüştürmenin Math.floor() gibi davranacağını, kayan noktadan sonraki değerleri kaldıracağını not edin.
Bir float veya int'i bool'a dönüştürmek, değişken sıfıra eşit değilse true döndürür.#### yapıcı
Değişken tipleri aynı zamanda kendi sınıf yapıcılarıdır; aslında bir
float değişkeni, bir _Float_ sınıfının _örneği_ olarak düşünülebilir.Bu tanımlamalar eşit derecede geçerlidir:
glslint i = 1;
int i = int( 1 );
int i = int( 1.9995 );
int i = int( true );
Bu, skaler tipler için çok anlam ifade etmeyebilir, tip dönüştürmeden çok farklı değildir, ancak aşırı yükleme bölümüne değinirken mantıklı olacaktır.Tamam, bunlar 3
temel tür, onsuz yaşayamayacağınız şeyler ama elbette GLSL'nin sunacağı daha fazlası var.Vektörler
JavaScript'te olduğu gibi GLSL'de de verileri işlemenin daha karmaşık yollarına ihtiyacınız olacak, işte burada
vektörler devreye girer.
JavaScript'te bir x ve y değerini bir arada tutmak için bir Point sınıfı kodladığınızı varsayıyorum, bunun kodu şöyle olurdu:
glsl// 'sınıf' tanımı:
var Point = function( x, y ){
this.x = x || 0;
this.y = y || 0;
}//ve şöyle örneklendirirdiniz:
var p = new Point( 100,100 );
Az önce gördüğümüz gibi, bu PEKÇOK seviyede ÇOK yanlış! Bir kere o
var anahtar kelimesi, sonra korkunç this, sonra yine tipsiz x ve y değerleri...
Hayır, bu shader dünyasında işe yaramayacak.Bunun yerine, GLSL verileri bir arada tutmak için yerleşik veri yapıları sunar:
*
bvec2: 2B Boolean vektör, bvec3: 3B Boolean vektör, bvec4: 4B Boolean vektör
* ivec2: 2B Tam sayı vektör, ivec3: 3B Tam sayı vektör, ivec4: 4B Tam sayı vektör
* vec2: 2B Float vektör, vec3: 3B Float vektör, vec4: 4B Float vektörHer temel tür için bir tür vektör olduğunu hemen fark ettiniz, zeki tavşan.
Az önce gördüklerimizden, bir
bvec2'nin iki bool tipi değer ve bir vec4'ün dört float değer tutacağını çıkarabilirsiniz.Vektörlerin tanıttığı bir diğer şey boyut sayısıdır, bu 2B grafik render ederken 2B vektör ve 3B yaparken 3B vektör kullanıldığı anlamına gelmez.
O zaman 4B vektör neyi temsil eder? (aslında buna tesseract veya hiperküp denir)
Hayır, boyutlar vektörde saklanan bileşenlerin veya değişkenlerin sayısını ve tipini temsil eder:
glsl// bir 2B Boolean vektör oluşturalım
bvec2 b2 = bvec2 ( true, false );// bir 3B Tam sayı vektör oluşturalım
ivec3 i3 = ivec3( 0,0,1 );
// bir 4B Float vektör oluşturalım
vec4 v4 = vec4( 0.0, 1.0, 2.0, 1. );
b2 iki farklı boolean değer, i3 3 farklı tam sayı değer ve v4 4 farklı float değer saklar.ama bu değerleri nasıl alırız?
skalerler durumunda, cevap açıktır; float f = 1.2; ile, f değişkeni 1.2 değerini tutar.
Vektörlerle biraz farklı ve oldukça güzeldir.#### erişimciler
Değerlere erişmenin farklı yolları var
glsl// bir 4B Float vektör oluşturalım
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );
4 değeri almak için şunları yapabilirsiniz:
glslfloat x = v4.x; // x = 0.0
float y = v4.y; // y = 1.0
float z = v4.z; // z = 2.0
float w = v4.w; // w = 3.0
güzel ve kolay; ama aşağıdakiler de verilerinize erişmenin eşit derecede geçerli yollarıdır:
glslfloat x = v4.x = v4.r = v4.s = v4[0]; // x = 0.0
float y = v4.y = v4.g = v4.t = v4[1]; // y = 1.0
float z = v4.z = v4.b = v4.p = v4[2]; // z = 2.0
float w = v4.w = v4.a = v4.q = v4[3]; // w = 3.0
Ve zeki tavşan olarak zaten üç şeyi fark ettiniz:
*
X, Y, Z & W 3B programlarda 3B vektörleri temsil etmek için kullanılır
* R, G, B & A renkleri ve alfa değerini kodlamak için kullanılır
* [0], [1], [2] & [3] rastgele erişimli bir değer dizimiz olduğu anlamına gelirYani 2B veya 3B koordinatları, alfa değeri olan veya olmayan bir rengi veya sadece bazı rastgele değişkenleri manipüle etmenize bağlı olarak, en uygun vektör tipini ve boyutunu seçebilirsiniz.
Tipik olarak 2B koordinatlar ve vektörler (geometrik anlamda)
vec2, vec3 veya vec4 olarak, renkler opaklığa ihtiyacınız varsa vec3 veya vec4 olarak saklanır, ancak vektörlerin nasıl kullanılacağı konusunda kısıtlama yoktur.
Örneğin, bir bvec4'te sadece bir boolean değer saklamak isterseniz, mümkündür, sadece bellek israfıdır.not: bir shader'da, renk değerleri (
R, G, B & A) normalleştirilmiştir, 0 ile 1 arasında değişirler, 0 ile 0xFF arasında değil, bu yüzden onları saklamak için Tam sayı ivec4 yerine Float vec4 kullanmanız daha iyi olur.Güzel, ama daha fazlası var!
#### swizzle
Aynı anda birden fazla değer döndürmek mümkündür; diyelim ki bir
vec4'ün sadece X ve Y değerlerine ihtiyacınız var, JavaScript'te şöyle bir şey yazmanız gerekirdi:
glslvar needles = [0, 1]; // veri yapımızdaki 'x' & 'y'nin konumu
var a = [ 0,1,2,3 ]; // 'vec4' veri yapımız
var b = a.filter( function( val, i, array ) {
return needles.indexOf( array.indexOf( val ) ) != -1;
});
// b = [ 0, 1 ]//veya daha gerçek anlamda:
var needles = [0, 1];
var a = [ 0,1,2,3 ]; // 'vec4' veri yapımız
var b = [ a[ needles[ 0 ] ], a[ needles[ 1 ] ] ]; // b = [ 0, 1 ]
Çirkin. GLSL'de şöyle alabilirsiniz:
glsl// bir 4B Float vektör oluştur
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );//ve sadece X & Y bileşenlerini al
vec2 xy = v4.xy; // xy = vec2( 0.0, 1.0 );
Ne oldu?! Erişimcileri birleştirdiğinizde, GLSL zarif bir şekilde istediğiniz değerlerin bir alt kümesini en uygun vektör formatında döndürür.
Gerçekten, vektör isterseniz JavaScript'teki bir dizi gibi rastgele erişimli bir veri yapısıdır.
Yani sadece verilerinizin bir alt kümesini almakla kalmaz, aynı zamanda ihtiyacınız olan sırayı da belirtebilirsiniz, bu bir vektörün bileşenlerinin değerlerini ters çevirecektir:
glsl// bir 4B Float vektör oluştur: R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );//ve renk bileşenlerini A,B,G,R sırasında al
vec4 backwards = color.abgr; // backwards = vec4( 1.0, 0.0, 0.8, 0.2 );
Ve tabii ki aynı bileşeni birden fazla kez isteyebilirsiniz:
glsl// bir 4B Float vektör oluştur: R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );//ve rengin G & A kanallarına dayalı bir GAG vec3 al
vec3 GAG = color.gag; // GAG = vec4( 0.8, 1.0, 0.8 );
Bu, vektörlerin parçalarını birleştirmek, bir RGBA rengin sadece rgb kanallarını çıkarmak vb. için son derece kullanışlıdır.
#### her şeyi aşırı yükle!
Tipler bölümünde yapıcı hakkında bir şey söylemiştim ve bu yine GLSL'nin harika bir özelliği; aşırı yükleme.
Bilmeyenler için, bir operatörü veya fonksiyonu aşırı yüklemek kabaca şu anlama gelir: _'söz konusu operatörün veya fonksiyonun davranışını operandlara/argümanlara göre değiştirmek'_.
JavaScript'te aşırı yüklemeye izin verilmez, bu yüzden başta biraz tuhaf gelebilir ama eminim alıştıktan sonra JS'de neden uygulanmadığını merak edeceksiniz (kısa cevap, tipleme).
Operatör aşırı yüklemenin en temel örneği şöyledir:
glslvec2 a = vec2( 1.0, 1.0 );
vec2 b = vec2( 1.0, 1.0 );
//aşırı yüklenmiş toplama
vec2 c = a + b; // c = vec2( 2.0, 2.0 );
NE? Yani sayı olmayan şeyleri toplayabilir misiniz?!Evet, kesinlikle. Tabii ki bu tüm operatörler (
+, -, * & /) için geçerlidir ama bu sadece başlangıç.
Aşağıdaki parçayı düşünün:
glslvec2 a = vec2( 0.0, 0.0 );
vec2 b = vec2( 1.0, 1.0 );
//aşırı yüklenmiş yapıcı
vec4 c = vec4( a , b ); // c = vec4( 0.0, 0.0, 1.0, 1.0 );
İki vec2'den bir vec4 oluşturduk, bunu yaparken yeni vec4, a.x ve a.y'yi c'nin X, Y bileşenleri olarak kullandı.
Sonra b.x ve b.y'yi aldı ve c'nin Z ve W bileşenleri olarak kullandı.Bu, bir fonksiyonun farklı argümanları kabul etmek üzere aşırı yüklenmesidir, bu durumda
vec4 yapıcısı.
Bu, aynı metodun farklı imzalara sahip birçok versiyonunun aynı programda bir arada var olabileceği anlamına gelir, örneğin aşağıdaki tanımlamaların hepsi geçerlidir:
glslvec4 a = vec4(1.0, 1.0, 1.0, 1.0);
vec4 a = vec4(1.0);// x, y, z, w hepsi 1.0'a eşit
vec4 a = vec4( v2, float, v4 );// vec4( v2.x, v2.y, float, v4.x );
vec4 a = vec4( v3, float );// vec4( v3.x, v3.y, v3.z, float );
vb.
Dikkat etmeniz gereken tek şey, vektörünüzü beslemek için yeterli argüman sağlamaktır.Son olarak, programınızda yerleşik fonksiyonları, tasarlanmadıkları argümanları alacak şekilde aşırı yüklemenize izin verilir (yine de bu çok sık olmamalıdır).
#### diğer tipler
Vektörler eğlencelidir, shader'ınızın eti sütüdür.
Matrisler ve Doku örnekleyicileri gibi başka temel türler de vardır, bunlar kitapta daha sonra ele alınacaktır.
Diziler de kullanabiliriz. Elbette tipli olmaları gerekir ve bazı tuhaflıklar vardır:
* sabit boyutludurlar
* push(), pop(), splice() vb. yapamazsınız ve
`length` özelliği yoktur
* değerlerle hemen başlatılamazlar
* değerleri tek tek ayarlamanız gerekirbu çalışmayacaktır:
glslint values[3] = [0,0,0];
ama bu çalışacaktır:
glslint values[3];
values[0] = 0;
values[1] = 0;
values[2] = 0;
Verilerinizi bildiğinizde veya küçük değer dizileriniz olduğunda bu iyidir.
Daha ifade edici bir değişken tanımlama yolu istiyorsanız,
bir de `struct` tipi var. Bunlar metotları olmayan _nesneler_ gibidir;
aynı nesne içinde birden fazla değişkeni saklamaya ve erişmeye izin verirler
glslstruct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
ardından _colors_ değerlerini şu şekilde ayarlayabilir ve alabilirsiniz:
glsl//yapıyı bazı değerlerle başlat
ColorStruct sandy = ColorStruct( vec3(0.92,0.83,0.60),
vec3(1.,0.94,0.69),
vec3(0.95,0.86,0.69) );//yapıdan bir değere eriş
sandy.color0 // vec3(0.92,0.83,0.60)
Bu sözdizimi şekeridir ama daha temiz, en azından daha aşina olduğunuz kod yazmanıza yardımcı olabilir.#### ifadeler & koşullar
Veri yapıları kendi başlarına güzeldir ama bir noktada yinelememiz veya koşullu testler yapmamız _gerekebilir_.
Neyse ki, sözdizimi JavaScript'e çok yakındır.
Bir koşul şöyledir:
glslif( condition ){
//doğru
}else{
//yanlış
}
Bir for döngüsü genellikle:
glslconst int count = 10;
for( int i = 0; i <= count; i++){
//bir şey yap
}
veya float yineleyici ile:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
//bir şey yap
}
`count`'un bir `sabit` olarak tanımlanması gerektiğini not edin.
Bu, tipin önüne bir `const` niteleyicisi eklemek anlamına gelir, bunu bir saniye içinde ele alacağız.ayrıca
`break` ve `continue` ifadelerimiz var:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
if( i < 5. )continue;
if( i >= 8. )break;
}
Bazı donanımlarda `break`'in beklendiği gibi çalışmadığını ve döngünün erken çıkmadığını unutmayın.Genel olarak, yineleme sayısını mümkün olduğunca düşük tutmak ve döngülerden ve koşullu ifadelerden mümkün olduğunca kaçınmak isteyeceksiniz.
#### niteleyiciler
Değişken tiplerinin üstüne, GLSL niteleyiciler kullanır.
Kısaca, niteleyiciler derleyicinin hangi değişkenin ne olduğunu bilmesine yardımcı olur.
Örneğin bazı veriler yalnızca CPU'dan GPU'ya sağlanabilir, bunlara attribute ve uniform denir.
Attribute'ler vertex shader'lar için ayrılmıştır, uniform'lar hem vertex hem de fragment shader'larda kullanılabilir.
Ayrıca vertex ve fragment shader arasında değişken geçirmek için kullanılan bir
`varying` niteleyicisi de vardır.Burada çok fazla detaya girmeyeceğim çünkü ağırlıklı olarak fragment shader'a odaklanıyoruz ama kitapta daha sonra şöyle bir şey göreceksiniz:
glsluniform vec2 u_resolution;
Burada ne yaptığımızı görüyor musunuz? Değişkenin tipinden önce bir `uniform` niteleyicisi koyduk
Bu, üzerinde çalıştığımız tuvalin çözünürlüğünün CPU'dan shader'a geçirildiği anlamına gelir.
Tuvalin genişliği x'te ve yüksekliği 2B vektörün y bileşeninde saklanır.Derleyici bu niteleyicinin önünde bir değişken gördüğünde, çalışma zamanında bu değerleri ayarlayamayacağınızdan emin olacaktır.
Aynı şey
`for` döngümüzün sınırı olan `count` değişkenimiz için de geçerlidir:
glslconst float count = 10.;
for( ... )
Bir `const` niteleyicisi kullandığımızda, derleyici değişkenin değerini yalnızca bir kez ayarlamamızı sağlayacaktır, aksi takdirde sabit değildir.Fonksiyon imzalarında kullanılan 3 ek niteleyici vardır:
`in`, `out` ve `inout`.
JavaScript'te, bir fonksiyona skaler argümanlar geçirdiğinizde, değerleri salt okunurdur ve fonksiyonun içinde değerlerini değiştirirseniz,
değişiklikler fonksiyonun dışındaki değişkene uygulanmaz.
glslfunction banana( a ){
a += 1;
}
var value = 0;
banana( value );
console.log( value );// > 0 ; değişiklikler fonksiyonun dışında hesaba katılmaz
Argüman niteleyicileri ile argümanların davranışını belirtebilirsiniz:
*
`in` salt okunur olacaktır ( varsayılan )
* `out` salt yazılır: bu argümanın değerini okuyamazsınız ama ayarlayabilirsiniz
* `inout` okuma-yazma: bu değişkenin değerini hem alabilir hem de ayarlayabilirsinizGLSL'de banana metodunu yeniden yazmak şöyle görünür
glslvoid banana( inout float a ){
a += 1.;
}
float A = 0.;
banana( A ); //artık A = 1.;
Bu JS'den çok farklı ve oldukça güçlüdür ama imza niteleyicilerini belirtmek zorunda değilsiniz (varsayılan salt okunurdur).#### uzay & koordinatlar
Son not, DOM ve Canvas 2D'de Y ekseninin 'aşağı' yönünü göstermesine alışığızdır.
Bu, bir web sayfasının açılma şeklini takip ettiğinden DOM bağlamında mantıklıdır; navigasyon çubuğu üstte, içerik alta doğru genişler.
WebGL tuvalinde Y ekseni ters çevrilmiştir: Y 'yukarı' yönünü gösterir.
Bu, orijinin, (0,0) noktasının, bir 2B Tuvalin sol üst köşesinde olduğu gibi değil, bir WebGL bağlamının sol alt köşesinde yer aldığı anlamına gelir.
Doku koordinatları bu kuralı takip eder ve başlangıçta sezgisel olmayabilir.
Ve bitti!
Elbette çeşitli kavramların daha derinlerine inebilirdik ama daha önce belirtildiği gibi, bu yeni gelenlere BÜYÜK BİR KUCAKLAMA vermeyi amaçlıyor.
Sindirmesi gereken epeyce şey var ama sabır ve pratikle, bu giderek daha doğal hale gelecektir.Umarım bunların bir kısmını faydalı bulmuşsunuzdur, şimdi kitaptaki yolculuğunuza başlamaya ne dersiniz?
---
Appendix/04/README Ua (appendix/04/README-ua.md)
Вступ для тих, хто прийшов із JS
автор Nicolas Barradeau
Якщо ви JavaScript-розробник, швидше за все, ви будете трохи спантеличені, читаючи книгу.
Дійсно, є багато відмінностей між маніпулюванням високорівневим JS і длубанням у менш високорівневих шейдерах.
Проте, на відміну від більш низькорівневої мови асемблера, GLSL є людино-зрозумілою мовою, і я впевнений, що як тільки ви розберетеся з її особливостями, то швидко запрацюєте.
Я припускаю, що ви маєте хоча б поверхневе знання про JavaScript і API Canvas.
Якщо ні, не хвилюйтеся, ви все одно зможете зрозуміти більшу частину цього розділу.
Крім того, я не буду надто вдаватися в подробиці, і деякі речі можуть бути _напівправдою_, тож не розраховувайте на "вичерпний посібник", а радше на ознайомлення.
Введення для новачків
JavaScript чудово підходить для швидкого прототипування; ви накидуєте купу різноманітних нетипізованих змінних і методів, можете динамічно додавати та видаляти члени класів, оновлювати сторінку і швидко перевіряти чи вона працює.
Потім можна внести нові зміни, оновити сторінку, повторити - легке життя.
Тож ви можете поставити собі питання, у чому різниця між JavaScript і GLSL?
Зрештою, обидва працюють у браузері, обидва використовуються для малювання купи прикольних штук на екрані, і в цьому сенсі JS легше використовувати.
Що ж, головна відмінність полягає в тому, що Javascript є інтерпретованою мовою, тоді як GLSL є компільованою.
Скомпільована програма виконується нативно в ОС, є низькорівневою і загалом швидкою.
Інтерпретована програма потребує для свого виконання віртуальну машину (VM), є високорівневою і відносно більш повільною.
Коли браузер (_VM для JavaScript_) виконує або інтерпретує фрагмент JS-коду, він ще не має уявлення про те, яка змінна чим являється і яка функція що робить (за винятком типізованих масивів).
Тож він не може нічого оптимізувати _наперед_.
Потрібен деякий час, щоб прочитати ваш код, щоб вивести, на основі використання, типи ваших змінних та методів і, коли це можливо, перетворити _дещо_ з вашого коду на код асемблера, який виконуватиметься набагато швидше.
Це повільний, кропіткий і шалено складний процес. Якщо вас цікавлять деталі, я рекомендую подивитися, як працює рушій V8 у Chrome.
Найгірше те, що кожен браузер оптимізує JS по-своєму, а процес _прихований_ від вас. В цьому плані ви безсилі.
Компільована програма не потребує інтерпретації. Операційна система запускає її і якщо програма валідна, вона виконується.
Це велика відмінність. Якщо ви забули крапку з комою в кінці рядка, ваш код вже не валідний та не скомпілюється, він взагалі не перетвориться на програму.
Це суворо, але це те, чим є шейдер: _програмою, що компілюється для виконання на GPU_.
Не бійтеся! Компілятор який перевіряє правильність вашого коду, стане вашим найкращим другом.
Приклади цієї книги та редактор коду дуже дружні до користувача.
Вони підкажуть вам де і чому не вдалося скомпілювати вашу програму.
Потім, після потрібних виправлень, коли шейдер буде готовий до компіляції, він миттєво зобразить результат своєї роботи. Це чудовий спосіб навчання, оскільки він дуже наочний і безпечний, бо насправді ви нічого не зможете зламати.
Останнє зауваження: шейдер складається з 2 програм: вершинного шейдера і фрагментного шейдера.
Якщо коротко, то вершинний шейдер це перша програма, що отримує на вхід геометрію та перетворює її на серію пікселів (або фрагментів).
Потім вона передає їх до фрагментного шейдера - другої програми, яка вирішить, яким кольором пофарбувати ці пікселі.
Дана книга здебільшого зосереджена на фрагментних шейдерах. У всіх прикладах геометрія є простим чотирикутником, який охоплює весь екран.
Готові?
Поїхали!
Сильні типи
Коли ви приходите з JS або будь-якої іншої нетипізованої мови, типізація змінних являється для вас чужорідною концепцією, що стає найважчим кроком до GLSL.
Типізація, як випливає з назви, означає, що вам потрібно надавати тип усім своїм змінним і функціям.
Це фактично означає, що слів
var або let більше не існує.
Поліція думок GLSL стерла їх із загальної мови й ви більше не можете їх використовувати, тому що, ну... їх не існує.Замість використання чарівного слова
var вам доведеться _явно вказати тип кожної змінної_, яку ви використовуєте, тоді компілятор бачитиме лише ті об'єкти та примітиви, з якими він вміє ефективно поводитися.
Мінус, що ви не можете використовувати ключове слово var і повинні _явно вказувати всі типи_, полягає в тому, що вам доведеться знати типи усіх змінних і знати їх добре.
Будьте спокійні, їх небагато і вони досить прості (GLSL це вам не Java-фреймворк).Це може здатися страшним, але загалом це не дуже відрізняється від того, що ви робите на JavaScript. Наприклад, якщо ви маєте
boolean-змінну, то ви очікуєте, що вона зберігатиме лише true або false і нічого більше.
Якщо змінна називається var uid = XXX;, то в ній вірогідно зберігається цілочисельне значення, а var y = YYY; _може_ бути посиланням на значення з рухомою крапкою.
Що ще краще, завдяки сильним типам ви не витрачатимете час на роздуми про те, чи X == Y (чи typeof X == typeof Y?, чи typeof X !== null && Y...). Ви просто знаєте це, а якщо ні, то компілятор знатиме напевно.Ось скалярні типи (скаляр описує величину), які можна використовувати в GLSL:
bool (булів тип), int (ціле число), float (число з рухомою крапкою).
Є й інші типи, але не будемо поспішати. Наступний фрагмент показує, як оголосити vars (так, я використав заборонене слово) у GLSL:
glsl// логічне значення
JS: var b = true; GLSL: bool b = true;// цілочисельне значення
JS: var i = 1; GLSL: int i = 1;
// числове значення з рухомою крапкою
JS: var f = 3.14159; GLSL: float f = 3.14159;
Не дуже важко, правда? Як згадувалося вище, це навіть полегшує роботу, оскільки ви не витрачаєте час на перевірку типів даних змінних.
Якщо це здається сумнівним, то пам'ятайте, що ви робите це для того, щоб ваша програма виконувалася набагато швидше, ніж на JS.#### void
Тип
void приблизно відповідає null. Він використовується в якості типу, який має повертати метод, коли він нічого не повертає.
Ви не можете призначати його змінним.#### boolean
Як вам відомо, логічні значення здебільшого використовуються в умовних перевірках: "
if (myBoolean == true) {...} else {...}".
Якщо умовне розгалуження є звичайним підходом для CPU, то для паралельної природи GLSL це твердження є менш правдивим.
Використання умовних галужень навіть не рекомендується у більшості випадків і у книзі показано кілька альтернативних методів для вирішення цього обмеження.#### приведення типів
Як казав Боромир, "не можна просто взяти та скомбінувати типізовані примітиви". На відміну від JavaScript, GLSL не дозволить вам виконувати операції між змінними різних типів.
Наприклад, цей код:
glslint i = 2;
float f = 3.14159;// спроба помножити ціле число на значення з рухомою крапкою
float r = i * f;
не спрацює, тому що ви намагаєтеся схрестити _кота_ і _жирафа_.
Проблема вирішується за допомогою приведення типу, що _змусить компілятор повірити_, що i має тип float без фактичної зміни типу i.
glsl// приведення типу цілочисельної змінної 'i' до float
float r = float(i) * f;
Це як вдягти _кота_ у шкіру _жирафа_ і працюватиме належним чином (змінна
r зберігатиме результат множення i на f).Можна привести будь-який зі згаданих вище типів до будь-якого іншого. Зауважте, що приведення
float до int поводитиметься як Math.floor(), оскільки видалятиме значення після рухомої крапки.
Приведення float або int до bool поверне true, якщо змінна не дорівнює нулю.#### конструктор
Типи змінних також є конструкторами класів для самих себе. Змінну
float фактично можна розглядати як _екземпляр_ класу _float_.Наступні оголошення рівнозначно валідні:
glslint i = 1;
int i = int(1);
int i = int(1.9995);
int i = int(true);
Це може здатися не дуже схожим на скалярні типи та не дуже відрізняється від приведення типів, але у цьому з'явиться більше сенсу, коли дійдемо до розділу перевантаження.Отже, ми познайомилися з трьома
примітивними типами, без яких ви не зможете жити, але, звісно, GLSL має й інші.Вектори
У Javascript, як і в GLSL, вам знадобляться складніші способи маніпуляції даними, ось де
вектори стануть у пригоді.
Я припускаю, що вам вже доводилося писати клас Point на JavaScript, для утримання значень x і y, що виглядав якось так:
glsl// визначення класу:
var Point = function(x, y) {
this.x = x || 0;
this.y = y || 0;
}// створення екземляру:
var p = new Point(100, 100);
Як ми щойно побачили, це ДУЖЕ неправильно на ДУЖЕ багатьох рівнях! По-перше, ключове слово
var, далі жахливе this, потім знову нетипізовні значення x і y...
Ні, це не запрацює в шейдерленді.Натомість GLSL надає вбудовані структури даних для їх групування, а саме:
*
bvec2: 2D вектор для bool, bvec3: 3D вектор для bool, bvec4: 4D вектор для bool
* ivec2: 2D вектор для int, ivec3: 3D вектор для int, ivec4: 4D вектор для int
* vec2: 2D вектор для float, vec3: 3D вектор для float, vec4: 4D вектор для floatВи відразу помітили, що для кожного примітиву є відповідний векторний тип.
З показаного вище, ви можете зробити висновок, що
bvec2 буде містити два значення типу bool, а vec4 — чотири значення float.Також вектори вводять таку річ як вимірність або розмірність. Це не означає, що 2D-вектор використовується при малюванні 2D-графіки, а 3D-вектор при малюванні 3D-сцен. Ні!
Що в такому разі буде представляти 4D-вектор? (ну насправді це називається тесерактом або гіперкубом)
Розмірність представляє кількість і тип компонентів або змінних, що зберігаються у векторі:
glsl// створюємо двовимірний булів вектор
bvec2 b2 = bvec2(true, false);// створюємо тривимірний цілочисельний вектор
ivec3 i3 = ivec3(0, 0, 1);
// створюємо чотиривимірний вектор з рухомою комою
vec4 v4 = vec4(0.0, 1.0, 2.0, 1.0);
b2 зберігає два різних булевих значення, i3 - 3 різні цілочисельні значення, а v4 - 4 різні значення з рухомою комою.Але як звернутися до цих значень?
У випадку
скалярів відповідь очевидна: для "float f = 1.2;", змінна f містить значення 1.2.
З векторами це трохи інакше і доволі красиво.#### аксесори - доступи до елементів
Існують різні способи доступу до значень
glsl// створимо чотиривимірний вектор типу float
vec4 v4 = vec4(0.0, 1.0, 2.0, 3.0);
отримати кожне з 4-х значень, можна наступним чином:
glslfloat x = v4.x; // x = 0.0
float y = v4.y; // y = 1.0
float z = v4.z; // z = 2.0
float w = v4.w; // w = 3.0
Просто і легко. Наведені нижче приклади показують інші еквівалентні способи доступу до цих даних:
glslfloat x = v4.x = v4.r = v4.s = v4[0]; // x = 0.0
float y = v4.y = v4.g = v4.t = v4[1]; // y = 1.0
float z = v4.z = v4.b = v4.p = v4[2]; // z = 2.0
float w = v4.w = v4.a = v4.q = v4[3]; // w = 3.0
Кмітливий читач міг помітити три речі:
*
X, Y, Z, W зазвичай використовуються в 3D-програмах для представлення 3D-векторів
* R, G, B, A використовуються для кодування кольорів і альфа-каналу
* [0], [1], [2], [3] означає, що ми можемо звертатися до значень через індексиТож залежно від того, чи працюєте ви з дво- чи тривимірними координатами, кольором з альфа-каналом чи без нього, або просто з деякими довільними значеннями, ви можете вибрати найбільш відповідний тип і розмірність вектора.
Зазвичай двовимірні координати та вектори (в геометричному сенсі) зберігаються як
vec2, vec3 або vec4, кольори як vec3 або vec4, якщо вам потрібна непрозорість. Але, в цілому, обмежень на те як використовувати вектори немає.
Наприклад, якщо ви хочете зберегти лише одне логічне значення в bvec4, це можливо, але буде марною витратою пам'яті.Примітка: у шейдері значення кольорів (
R, G, B, A) нормалізовані, тобто варіюються в діапазоні від 0 до 1, а не від 0 до 0xFF, тому для них краще використовувати тип float vec4, ніж цілочисельний тип ivec4.Вже маємо хороший початок, але рушаймо далі!
#### змішування
З вектора можна отримати більше одного значення за раз. Скажімо, із
vec4 вам потрібні лише значення X і Y. У JavaScript вам довелося б написати щось подібне:
glslvar needles = [0, 1]; // розміщення 'x' і 'y' в нашій структурі даних
var a = [0, 1, 2, 3]; // наша структура даних 'vec4'
var b = a.filter(function(val, i, array) {
return needles.indexOf(array.indexOf(val)) != -1;
});
// b = [0, 1]// або більш буквально:
var needles = [0, 1];
var a = [0, 1, 2, 3]; // структура даних 'vec4'
var b = [a[needles[0]], a[needles[1]]]; // b = [0, 1]
Виглядає потворно. У GLSL ви можете отримати ці дані так:
glsl// створюємо 4D-вектор типу float
vec4 v4 = vec4(0.0, 1.0, 2.0, 3.0);// і одночасно отримуємо лише X та Y
vec2 xy = v4.xy; // xy = vec2(0.0, 1.0);
Що це щойно сталося?! Коли ви об'єднуєте аксесори, GLSL елегантно повертає підмножину значень, які ви запросили, у найбільш відповідному векторному форматі.
Тож тут вектор — це структура даних із довільним доступом, схожий на масив як у JavaScript.
Таким чином, ви можете не тільки отримати підмножину ваших даних, але і вказати їх порядок, у якому вони вам потрібні. Наступний приклад змінює порядок отримання компонентів вектора:
glsl// створюємо чотиривимірний вектор типу float: R,G,B,A
vec4 color = vec4(0.2, 0.8, 0.0, 1.0);// отримуємо компоненти кольору в порядку A,B,G,R
vec4 backwards = color.abgr; // backwards = vec4(1.0, 0.0, 0.8, 0.2);
І, звичайно, ви можете повернути той самий компонент кілька разів:
glsl// створюємо чотиривимірний вектор типу float: R,G,B,A
vec4 color = vec4(0.2, 0.8, 0.0, 1.0);// отримуємо vec3 з компонентами GAG на основі каналів G і A з початкового кольору
vec3 GAG = color.gag; // GAG = vec4(0.8, 1.0, 0.8);
Це надзвичайно зручно для об'єднання частин вектору, виділення лише rgb-каналів із кольору RGBA тощо.
#### перевантаження
У розділі типів я згадував про конструктор і можливість перевантаження.
Для тих, хто не знає, перевантаження оператора або функції означає _'зміну поведінки зазначеного оператора або функції залежно від операндів/аргументів'_.
В JavaScript немає перевантаження, тому спочатку воно може здатися трохи дивним, але я впевнений, що як тільки ви звикнете до нього, то будете дивуватися, чому це не реалізовано в JS (коротка відповідь - типізація).
Найпростіший приклад перевантаженого оператора виглядає так:
glslvec2 a = vec2(1.0, 1.0);
vec2 b = vec2(1.0, 1.0);
// перевантажене додавання
vec2 c = a + b; // c = vec2(2.0, 2.0);
ЩО? Отже, можна додавати сутності, які не є числами?!Саме так. Також це стосується всіх операторів (
+, -, * і /), але це тільки початок.
Розглянемо наступний фрагмент:
glslvec2 a = vec2(0.0, 0.0);
vec2 b = vec2(1.0, 1.0);
// перевантажений конструктор
vec4 c = vec4(a, b); // c = vec4(0.0, 0.0, 1.0, 1.0);
Ми створили vec4 з двох vec2. Таким чином новий vec4 використав a.x і a.y як Xі Y компоненти та b.x і b.y як Z і W компоненти для вектора c.Ось що відбувається, коли функція перевантажується для прийняття різних аргументів, у попередньому випадку це був конструктор
vec4.
Це означає, що в одній програмі може співіснувати багато версій одного і того самого методу з різною сигнатурою. Наприклад, усі наступні оголошення є валідними:
glslvec4 a = vec4(1.0, 1.0, 1.0, 1.0);
vec4 a = vec4(1.0); // x, y, z, w - усі дорівнюють 1.0
vec4 a = vec4(v2, float, v4); // vec4(v2.x, v2.y, float, v4.x);
vec4 a = vec4(v3, float); // vec4(v3.x, v3.y, v3.z, float);
тощо
Єдине про що ви повинні попіклуватися, так це про передачу достатньої кількості аргументів для заповнення вектору.Нарешті, ви також можете перевантажувати вбудовані функції у вашій програмі, щоб вони могли приймати аргументи, для яких не були розроблені (хоча це не повинно траплятися занадто часто).
#### більше типів
Вектори прикольні і є основою вашого шейдера.
А ще існують інші примітиви, такі як матриці та текстурні семплери, які будуть розглянуті пізніше в книзі.
Ми також можемо використовувати масиви. Звичайно, їх потрібно типізувати й вони мають свої особливості у порівнянні з JS:
* мають фіксований розмір
* ви не можете використовувати методи push(), pop(), splice() тощо, і немає властивості
`lengthtext* ви не можете відразу ініціалізувати їх значеннями
* Ви повинні встановити значення індивідуальноЦе не спрацює:
glsl
int values[3] = [0, 0, 0];
textА ось це спрацює:
glsl
int values[3];
values[0] = 0;
values[1] = 0;
values[2] = 0;
textДобре, коли ви знаєте свої дані або маєте невеликі масиви значень.
Якщо вам потрібно більше виразності, то можете скористатися типом
struct`. Це як _об'єкти_ без методів.
Вони дозволяють зберігати та отримувати доступ до кількох змінних всередині одного об'єкта:
glslstruct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
Ви можете створити та отримати значення _colors_ наступним чином:
glsl// ініціалізуємо структуру деякими значеннями
ColorStruct sandy = ColorStruct(
vec3(0.92, 0.83, 0.60),
vec3(1., 0.94, 0.69),
vec3(0.95, 0.86, 0.69)
);// отримуємо доступ до значень із структури
sandy.color0 // vec3(0.92, 0.83, 0.60)
Це синтаксичний цукор, але він може допомогти написати чистіший код, принаймні більш звичний для вас.#### вирази та умови
Структури даних корисні, але нам _може_ знадобитися можливість для повторення дії або виконання умовних перевірок.
На щастя для нас, синтаксис дуже близький до JavaScript.
Умова виглядає так:
glslif (condition) {
// true
} else {
// false
}
Звичайни цикл for:
glslconst int count = 10;
for (int i = 0; i <= count; i++) {
// do something
}
Приклад циклу з ітератором типу float:
glslconst float count = 10.;
for (float i = 0.0; i <= count; i += 1.0) {
// do something
}
Зауважте, що `count` потрібно визначити як `константу`.
Це означає перед змінною потрібно додати кваліфікатор `const`, який ми розглянемо трохи згодом.Нам також доступні оператори
`break` і `continue`:
glslconst float count = 10.;
for (float i = 0.0; i <= count; i += 1.0) {
if (i < 5.) continue;
if (i >= 8.) break;
}
Зауважте, що на деяких типах пристроїв `break` не працює належним чином і завчасно не перериває виконання циклу.Загалом, кількість ітерацій має бути якомога меншою, та і в цілому бажано уникати використання циклів і умовних галужень.
#### кваліфікатори
Окрім типів змінних, GLSL використовує кваліфікатори.
Коротко кажучи, кваліфікатори допомагають повідомити компілятору призначення змінних.
Наприклад, деякі дані для GPU можуть бути надані тільки від CPU і називаються атрибутами та уніформами.
Атрибути використовуються у вершинних шейдерах, а уніформи можна використовувати як у вершинних, так і у фрагментних шейдерах.
Існує також кваліфікатор
`variying`, який використовується для передачі змінних від вершинного шейдеру до фрагментного.Я не буду вдаватися в деталі, оскільки ми зосереджені на фрагментних шейдерах, але далі в книзі ви побачите щось на кшталт:
glsluniform vec2 u_resolution;
Бачите, що ми тут зробили? Ми додали кваліфікатор `uniform` перед типом змінної.
Це означає, що змінна, яка відповідає за роздільну здатність полотна з яким ми працюємо, передається шейдеру з CPU.
Ширина полотна зберігається в x, а висота в y-компоненті даного 2D-вектора.Коли компілятор бачить змінну, якій передує цей кваліфікатор, він простежить, щоб ви не могли змінити такі значення під час рантайму.
Те саме стосується нашої змінної
`count`, яка слугувала обмеженням для циклу `for`:
glslconst float count = 10.;
for ( ... )
Коли ми використовуємо кваліфікатор `const`, компілятор не дає змогу перезаписувати значення такої змінної, інакше вона не була б константою.У сигнатурах функцій можуть використовуватися 3 додаткові кваліфікатори:
`in`, `out` та `inout`.
У JavaScript передані до функції значення скалярних аргументів доступні лише для читання. Якщо ви змінюєте їхні значення всередині функції, то ці зміни не застосовуються до змінної поза функцією.
glslfunction banana(a) {
a += 1;
}var value = 0;
banana(value);
console.log(value); // 0 - значення за межами функції не змінилося
За допомогою кваліфікаторів перед аргументами ви можете вказати їх поведінку:
*
`in` - лише для читання (за замовчуванням)
* `out` - лише для запису: можна змінити, але не можна прочитати значення
* `inout` - читання і запис: можна і прочитати й встановити нове значенняПереписаний метод banana у GLSL виглядає так:
glslvoid banana(inout float a) {
a += 1.;
}float A = 0.;
banana(A); // тепер A = 1.;
Це дуже відрізняється від JS і є досить потужною можливістю, але вам не обов'язково вказувати кваліфікатори аргументів. За замовчуванням вони доступні лише для зчитування.#### простір і координати
Останнє зауваження: у DOM і Canvas 2D ми звикли, що вісь Y спрямована 'вниз'.
Це має сенс у контексті DOM, оскільки відповідає способу розгортання вебсторінки: панель навігації вгорі, а контент прокручується донизу.
У полотні WebGL вісь Y перевернута і вказує 'вгору'.
Це означає, що початок координат, точка (0, 0), знаходиться в нижньому лівому куті контексту WebGL, а не у верхньому лівому куті, як у 2D Canvas.
Координати текстур також дотримуються цього правила, що спочатку може бути контрінтуїтивним.
Ось і все!
Звісно, ми б могли більше заглибитися в різноманітні концепції, але, як згадувалося раніше, цей розділ написаний як просте введення для новачків.
Тут вже написано достатньо для того, щоб за деякий час переварити нові знання, але з терпінням і практикою ця мова ставатиме для вас все більш природною.Сподіваюся, цей матеріал був корисним для вас. А тепер як щодо початку вашої подорожі основною частиною книги?
---
Appendix/04/README Vi (appendix/04/README-vi.md)
Giới thiệu cho người đã biết Javascript
Tác giả: Nicolas BarradeauNếu bạn là developer Javascript, khả năng cao là bạn sẽ thấy hoang mang một chút khi đọc quyển sách này.
Thực tế là có rất nhiều điểm khác biệt khi code JavaScript vốn chỉ là bề nổi, so với việc phải đụng tới shader ở sâu bên dưới tảng băng chìm.
Tuy nhiên, không giống với ngôn ngữ nền tảng là Assembly, GLSL rất gần với ngôn ngữ mà con người có thể hiểu được, và tôi tin rằng một khi bạn đã nắm được các đặc tính của nó thì bạn sẽ bắt kịp rất nhanh thôi.
Coi như bạn đã biết về Javascript và cả Canvas API đi.
Mà nếu có chưa biết mấy thì cũng đừng lo, bạn vẫn sẽ hiểu được phần lớn nội dung của phần này thôi.
Tất nhiên tôi sẽ không đi sâu vào chi tiết và một vài điều tôi nói có thể _không chính xác hoàn toàn_, nên đừng kỳ vọng nó được như "cầm tay chỉ việc" mà hãy coi nó như
MỘT CÁI ÔM NỒNG ẤM
JavaScript rất thích hợp để thử nghiệm nhanh ; bạn chỉ việc viết vài hàm đơn giản, không có ràng buộc gì về kiểu dữ liệu, tuỳ ý thêm bớt các hàm của class, tải lại trang web là đã thấy ngay kết quả rồi,
sau đó lại sửa một tí, tải lại trang, cứ thế lặp lại, dễ như ăn kẹo.
Thế GLSL thì có gì khác JavaScript chứ.
Suy cho cùng thì cả 2 đều chạy trên trình duyệt mà, chúng đều vẽ một vài thứ lên màn hình đó thôi, mà riêng về khía cạnh đó thì dùng JavaScript dễ hơn.
Ừ thì, điểm khác biệt chính nằm ở chỗ Javascript là một ngôn ngữ thông dịch (interpreted) còn GLSL thì là một ngôn ngữ biên dịch (compiled).
Một chương trình biên dịch được thực thi trực tiếp bởi hệ điều hành, là một chương trình bậc thấp và thường chạy rất nhanh.
Còn một chương trình thông dịch thì lại cần một Máy ảo (Virtual Machine / VM) để thực thi, nó là một chương trình bậc cao và thường chậm hơn.
Khi một trình duyệt (chính xác phải là _máy ảo JavaScript_ mới đúng) thực thi hoặc thông dịch một đoạn mã, nó chẳng biết biến nào có ý nghĩa gì hay hàm này sẽ cho kết quả gì (ngoại trừ TypedArrays).
Vì thế nó chẳng thể tối ưu bất kỳ cái gì _trước khi thực thi_ cả, nó còn cần thời gian để dịch code của bạn này, rồi thì đoán xem kiểu dữ liệu của các biến là gì này và nếu được thì cố gắng chuyển một phần sang dạng mã Assembly để code chạy nhanh hơn.
Đó là cả một quá trình cồng kềnh, phức tạp và có phần lề mề, nếu bạn hứng thú đi vào chi tiết thì tôi xin giới thiệu tìm hiểu cách mà trình thông dịch V8 của Chrome hoạt động.
Điểm tệ nhất đó là, mỗi trình duyệt lại tự tối ưu mã JavaScript theo một cách riêng mà quá trình này _hoàn toàn_ nằm ngoài tầm với của bạn.
Còn một chương trình biên dịch thì không như thế ; hệ điều hành thực thi nó, nếu chương trình không có lỗi biên dịch thì cứ thế mà chạy thôi.
Nếu bạn quên một dấu chấm phẩy cuối dòng thì khả năng cao là bạn sẽ được thông báo còn code của bạn thì thậm chí còn chưa được biên dịch thành chương trình cơ.
Hơi phũ, nhưng đó chính là cách mà shader hoạt động: _một chương trình được biên dịch để thực thi trên GPU_.
Đừng sợ! Một trình biên dịch sẽ là chiến hữu đáng tin cậy nhất của bạn.
Các ví dụ trong quyển sách này và người đồng hành editor online rất thân thiện.
Nó sẽ chỉ ra cho bạn thấy tại sao code của bạn không biên dịch được và bạn phải sửa chỗ nào, và nếu bạn làm đúng thì kết quả hiển thị ngay lập tức luôn.
Đó là một cách tuyệt vời để học vì nó rất trực quan và bạn chẳng phải sợ sẽ làm hỏng cái gì cả.
Điểm lưu ý cuối cùng, một chương trình shader được tạo nên bởi 2 chương trình con, đó là vertex shader và fragment shader.
Về cơ bản thì vertex shader sẽ nhận tham số đầu vào là các khối hình học rồi biến chúng thành các điểm ảnh (pixel) (hoặc fragment) rồi chuyển kết quả cho fragment shader xử lý tiếp, vốn công việc chính là tô màu từng điểm ảnh.
Quyển sách này hầu như chỉ tập trung vào chương trình thứ hai. Trong tất cả các ví dụ, khối hình được sử dụng chỉ là một tứ giác lấp đầy cả màn hình.
Vậy! Bạn đã sẵn sàng chưa ?
Tiếp tục nhé!
Quy định kiểu dữ liệu một cách chặt chẽ
Khi bạn đã quen với JavaScript hay các ngôn ngữ không quan trọng về kiểu dữ liệu, thì việc phải quy định kiểu dữ liệu cho mỗi biến là một khái niệm xa lạ, cũng khiến cho nó trở thành rào cản lớn nhất khi làm quen với GLSL.
Kiểu dữ liệu, như chính cái tên của nó, có nghĩa là bạn phải chỉ định mỗi biến (và cả hàm nữa) sử dụng kiểu dữ liệu gì.
Về cơ bản thì việc dùng chung một từ khoá
var cho tất cả các biến đã không còn nữa.
GLSL không cho phép điều đó xảy ra nên bạn có muốn cũng không được.Thay vì dùng từ khoá
var thần thánh, bạn sẽ phải _chỉ đích danh kiểu dữ liệu cho từng biến một_, sau đó thì trình biên dịch sẽ biết chính xác đang phải xử lý cái gì và làm thế nào thì hiệu quả nhất. Nhược điểm của việc này là bạn phải hiểu tất cả các kiểu dữ liệu, mà lại còn phải hiểu tường tận nữa cơ.
May thay, chỉ có một vài kiểu dữ liệu thôi và cũng khá đơn giản nữa.Nghe thì đáng sợ chứ thực ra nó không quá khác với code JavaScript mà bạn vẫn hay dùng đâu ; nếu một biến có kiểu
boolean thì bạn sẽ trông đợi nó chỉ lưu trữ một trong hai giá trị true hoặc false mà thôi.
Nếu một biến được khai báo là var uid = XXX;, thì có khả năng đó là một số nguyên còn nếu nó được khai báo là var y = YYY; có thể nó trỏ tới một số thực.
Còn với ngôn ngữ quy định kiểu dữ liệu (strong type), bạn sẽ không phí thời gian đoán xem 2 biến đó có cùng kiểu không, bằng các biểu thức X == Y (hay typeof X == typeof Y ? .. hoặc typeof X !== null && Y... ...) ; bạn sẽ biết chắc điều đó đúng hay sai mà kể cả bạn không để ý thì trình biên dịch sẽ làm thay việc đó.Đây là các kiểu dữ liệu đơn (scalar) trong GLSL:
bool (Đúng sai), int(Số nguyên), float(Số thực).
Còn vài kiểu nữa nhưng cứ từ từ, đoạn code mẫu dưới đây khai báo các biến (đừng quên var không tồn tại trong thế giới GLSL nhé):glsl// Khai báo một biến boolean
JavaScript: var b = true; GLSL: bool b = true;// Khai báo một số nguyên
JavaScript: var i = 1; GLSL: int i = 1;
// Khai báo một số thực
JavaScript: var f = 3.14159; GLSL: float f = 3.14159;
Không có gì khó phải không ? Như đã nói ở trên, nó thậm chí còn giúp bạn đỡ đau đầu khi code ấy chứ. Nếu còn nghi ngờ về điều đó thì cứ tạm bỏ qua, chỉ cần biết nó giúp chương trình của bạn chạy nhanh hơn JavaScript nhiều là cũng đủ rồi.
#### void
Có kiểu
void tương đương với null, nó được dùng khi hàm không trả về kết quả gì cả.
Và bạn không thể khai báo biến kiểu này.#### boolean
Các biến kiểu boolean hầu hết được sử dụng trong các câu lệnh điều kiện như ;
if( myBoolean == true ){}else{}.
Nếu các nhánh điều kiện rất hay gặp ở CPU, thì kiến trúc song song của GPU lại hạn chế đất diễn của chúng.
Thậm chí việc sử dụng các lệnh điều kiện còn không được khuyến khích ở đa phần các trường hợp, trong quyển sách này có một vài kỹ thuật để xử lý các trường hợp đó.#### Ép kiểu
Như Boromir đã nói, "One does not simply combine Typed primitives". Không như JavaScript, GLSL không cho phép thực hiện các phép toán giữa các toán hạng không cùng kiểu.
Ví dụ sau:
glslint i = 2;
float f = 3.14159;// thử nhân một số nguyên với một số thực
float r = i * f;
sẽ không cho kết quả tốt vì bạn đang cố lai con _mèo_ với con _hươu cao cổ_.
Giải pháp là ép kiểu (type casting) ; đoạn code sau sẽ giúp _trình biên dịch tin rằng_ i cũng có kiểu float dù i vẫn giữ nguyên kiểu vốn có:
glsl// ép biến i từ kiểu int sang float
float r = float( i ) * f;
Điều này giống như việc cho con _mèo_ mặc đồ của con _hươu cao cổ_ vậy, và nó sẽ có hiệu quả (
r sẽ kết quả của phép toán i x f).Bạn có thể ép kiểu qua lại giữa tất cả các kiểu phía trên, chú ý là khi chuyển từ số thực sang số nguyên thì phần thập phân sẽ biến mất, tương đương với việc dùng hàm
Math.floor(). Ép một số thực float hoặc một số nguyên int sang kiểu bool sẽ cho kết quả true nếu số đó khác 0.#### Hàm khởi tạo (constructor)
Kiểu dữ liệu của biến cũng chính là hàm khởi tạo của class tương ứng ; thực tế thì một số thực
float có thể coi là 1 _instance_ của class _Float_.Các lệnh sau đây đều hợp lệ và cho kết quả giống nhau
glslint i = 1;
int i = int( 1 );
int i = int( 1.9995 );
int i = int( true );
Trông thì không giống kiểu scalar lắm, và cũng na ná ép kiểu, nhưng mọi sự sẽ sáng tỏ ở phần overload.OK, vậy là ta đã biết về ba
kiểu dữ liệu cơ bản, những thứ mà bạn không thể sống nếu thiếu được và đương nhiên là GLSL còn nhiều kiểu khác.Vector
Trong cả JavaScript lẫn GLSL, bạn sẽ cần những cách tinh vi hơn để xử lý dữ liệu, và
vectors khi đó rất hữu ích.
Tôi cho rằng bạn đã sử dụng class Point trong JavaScript để lưu 2 giá trị x và y cùng lúc rồi, code sẽ trông thế này:
glsl// Khai báo 'class':
var Point = function( x, y ){
this.x = x || 0;
this.y = y || 0;
}// và bạn sẽ tạo một instance mới như sau
var p = new Point( 100,100 );
Như ta thấy, có quá nhiều điểm không hợp lý. Từ khoá
var vồn dùng cho biến thì lại được dùng để khai báo class rồi thì this chả hiểu ở đâu ra, xong lại x với y chả biết kiểu dữ liệu gì ...
Kiểu này là không ổn với shader đâu.Thay vào đó, GLSL có sẵn các cấu trúc dữ liệu để lưu trữ các biến đồng thời, có thể kể ra:
*
bvec2: Vector boolean 2 chiều, bvec3: Vector boolean 3 chiều, bvec4: Vector boolean 4 chiều
* ivec2: Vector số nguyên 2 chiều, ivec3: Vector số nguyên 3 chiều, ivec4: Vector số nguyên 4 chiều
* vec2: Vector số thực 2 chiều, vec3: Vector số thực 3 chiều, vec4: Vector số thực 4 chiềuBạn nhận ra là có đủ các loại vector cho mỗi kiểu dữ liệu cơ bản, tuyệt vời ông mặt giời.
Từ giải thích trên đây, ta có thể suy luận được rằng
bvec2 sẽ gồm 2 giá trị kiểu bool còn vec4 sẽ gồm 4 số thực kiểu floatMột điểm mới nữa từ vector là các chiều (dimension), không phải bạn dựng hình 2D thì dùng vector 2 chiều còn dựng hình 3D thì dùng vector 3 chiều đâu nhé. Nếu vậy thì vector 4 chiều dùng để dựng hình gì ? (Thực ra thì không gian 4 chiều có tên riêng đấy, là tesseract hoặc khối siêu lập phương - hypercube)
Nhưng không phải thế đâu nhé, từ chiều ở đây chỉ nói về số giá trị được lưu giữ trong mỗi vector thôi:
glsl// tạo một vector boolean 2 chiều
bvec2 b2 = bvec2 ( true, false );// tạo một vector số nguyên 3 chiều
ivec3 i3 = ivec3( 0,0,1 );
// tạo một vector số thực 4 chiều
vec4 v4 = vec4( 0.0, 1.0, 2.0, 1. );
b2 gồm 2 giá trị boolean, i3 gồm 3 số nguyên v4 gồm 4 số thực.Làm thế nào để sử dụng từng giá trị trong vector?
Với kiểu dữ liệu đơn thì quá đơn giản ; nếu ta có
float f = 1.2; thì biến f sẽ có giá trị 1.2.
Còn với vector thì hơi khác và cũng kỳ diệu hơn một chút.#### Truy cập
Có nhiều cách để truy cập các giá trị bên trong vector
glsl// Đầu tiên hãy tạo một vector số thực 4 chiều
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );
Để lấy từng giá trị trong vector, ta có thể viết:
glslfloat x = v4.x; // x = 0.0
float y = v4.y; // y = 1.0
float z = v4.z; // z = 2.0
float w = v4.w; // w = 3.0
rất dễ dàng, nhưng còn nhiều cách khác cũng cho kết quả tương tự:
glslfloat x = v4.x = v4.r = v4.s = v4[0]; // x = 0.0
float y = v4.y = v4.g = v4.t = v4[1]; // y = 1.0
float z = v4.z = v4.b = v4.p = v4[2]; // z = 2.0
float w = v4.w = v4.a = v4.q = v4[3]; // w = 3.0
Nếu bạn tinh ý thì sẽ nhận ra:
*
X, Y, Z và W được dùng để mô tả các vector trong đồ hoạ 3D
* R, G, B và A được dùng để mô tả các kênh màu và alpha
* [0], [1], [2] và [3] được dùng như một mảng cố địnhVậy nên tuỳ vào việc bạn đang xử lý các toạ độ 2D hay 3D, màu kèm theo alpha hoặc không, hay một vài con số bất kỳ, bạn có thể tuỳ chọn cách dùng vector bạn muốn.
Thường thì các toạ độ 2 chiều và các vector sẽ được lưu bằng các cấu trúc
vec2, vec3 hoặc vec4, màu sắc thì được lưu trong vec3 hoặc vec4 nếu bạn muốn lưu thêm kênh alpha nữa, không có ràng buộc nào cả.
Thậm chí, nếu bạn dùng cả một vector bvec4 chỉ để lưu 1 giá trị boolean thì cũng được nốt, có điều hơi lãng phí bộ nhớ.Chú ý: Trong shader, các giá trị của các kênh (
R, G, B, A) đều được chuẩn hoá để nằm trong khoảng [0-1] chứ không phải [0x00-0xFF], vì thế tốt nhất là nên sử dụng vector số thực 4 chiều vec4 để lưu trữ giá trị màu.Hay quá phải không, chưa hết đâu nhé!
#### Tráo đổi (swizzle)
Ta có thể lấy ra nhiều hơn một giá trị nữa cơ ; giả sử bạn chỉ cần 2 giá trị
X và Y từ một vec4, thì trong JavaScript, bạn sẽ phải làm như sau:
glslvar needles = [0, 1]; // vị trí của 'x' và 'y' trong array dưới đây
var a = [ 0,1,2,3 ]; // giả lập vec4
var b = a.filter( function( val, i, array ) {
return needles.indexOf( array.indexOf( val ) ) != -1;
});
// b = [ 0, 1 ]// hoặc trực diện luôn
var needles = [0, 1];
var a = [ 0,1,2,3 ]; // giả lập
vec4
var b = [ a[ needles[ 0 ] ], a[ needles[ 1 ] ] ]; // b = [ 0, 1 ]
Quá cồng kềnh. Hãy xem trong GLSL làm như thế nào:
glsl// Tạo một vec4
vec4 v4 = vec4( 0.0, 1.0, 2.0, 3.0 );// rồi chỉ lấy mỗi x và y ra
vec2 xy = v4.xy; // xy = vec2( 0.0, 1.0 );
Ủa cái gì vậy ?! Khi bạn truy cập liên tiếp (concatenate accessors), GLSL sẽ trả về tập con của các giá trị bạn muốn, gói gọn trong một vector khác.
Thực ra thì vector là một cấu trúc dữ liệu cho phép truy cập ngẫu nhiên, nếu muốn bạn có thể tượng tượng nó giống array bên JavaScript vậy.
Vì thế, bạn không chỉ lấy được 1 tập con mà còn có thể chỉ định thứ tự từng phần tử muốn lấy nữa cơ. Đoạn code sau sẽ tráo đổi giá trị của các vector theo thứ tự ngược lại:
glsl// Tạo một vector R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );// Truy cập các gía trị theo thứ tự ngược lại
vec4 backwards = v4.abgr; // backwards = vec4( 1.0, 0.0, 0.8, 0.2 );
Hơn thế nữa, chẳng ai ngăn bạn lấy một phần tử nhiều lần:
glsl// Tạo một vector R,G,B,A
vec4 color = vec4( 0.2, 0.8, 0.0, 1.0 );// Và tạo một vector mới chỉ dùng giá trị của kênh G (2 lần) và A
vec3 GAG = v4.gag; // GAG = vec4( 0.8, 1.0, 0.8 );
Khă năng này quá ngầu khi phải xử lý vector, ví dụ như khi chỉ muốn lấy các kênh RGB của một màu có đủ RGBA chẳng hạn.
#### Overload tất cả
Ở phần kiểu dữ liệu, tôi đã nhắc tới điều gì đó liên quan tới hàm khởi tạo (constructor) và đây lại là 1 tính năng tuyệt vời nữa của GLSL ; overload.
Cho ai chưa biết, overload là một toán tử hoặc hàm số đại loại sẽ _'tự động thay đổi cách thực thi sao cho khớp với kiểu dữ liệu'_.
JavaScript không có overload, nên bạn có thể thấy nó hơi lạ lúc đầu, nhưng khi đã quen rồi thì bạn sẽ thắc mắc sao JavaScript lại không có tính năng này (ngắn gọn là do không ràng buộc kiểu dữ liệu đó).
Hãy xem ví dụ đơn giản nhất để hiểu overload là gì:
glslvec2 a = vec2( 1.0, 1.0 );
vec2 b = vec2( 1.0, 1.0 );
// overload phép cộng
vec2 c = a + b; // c = vec2( 2.0, 2.0 );
HẢ ? Hai giá trị không phải số đơn thuần mà cũng cộng được ?!Chính xác là thế đó. Tất nhiên là áp dụng cho toàn bộ các toán tử khác (
+, -, * & /) nữa nhưng đây mới là mở đầu thôi.
Hãy xem đoạn code sau:
glslvec2 a = vec2( 0.0, 0.0 );
vec2 b = vec2( 1.0, 1.0 );
// overload hàm khởi tạo
vec4 c = vec4( a , b ); // c = vec4( 0.0, 0.0, 1.0, 1.0 );
Ta vừa mới tạo nên một vec4 từ 2 vec2, bằng cách gán giá trị của a.x và a.y cho X, Y của vec4, rồi lại gán tiếp b.x và b.y cho Z, W của vec4Đây là điều sẽ xảy ra khi một hàm được overload để chấp nhận các loại tham số khác nhau, cụ thể trong trường hợp này là hàm khởi tạo của
vec4.
Điều đó có nghĩa là rất nhiều phiên bản khác nhau của cùng 1 hàm có thể cùng tồn tại, ví dụ các lệnh khai báo sau hoàn toàn hợp lệ:glslvec4 a = vec4(1.0, 1.0, 1.0, 1.0);
vec4 a = vec4(1.0);// cả 4 giá trị x, y, z, w đều bằng 1.0
vec4 a = vec4( v2, float, v4 );// vec4( v2.x, v2.y, float, v4.x );
vec4 a = vec4( v3, float );// vec4( v3.x, v3.y, v3.z, float );
etc.
Điều duy nhất bạn cần bận tâm là đảm bảo hàm tạo có đủ dữ liệu nó cần mà thôi.
Điều cuối cùng, bạn hoàn toàn có thể overload một hàm có sẵn bất kỳ sao cho nó phù hợp với yêu cầu của bạn (cũng không nên lạm dụng quá).
#### Các kiểu dữ liệu khác
Vector thật thú vị, và là vũ khí chính trong code shader.
Còn các cấu trúc dữ liệu khác như Ma trận và Texture sampler sẽ được bàn tới ở phần sau của quyển sách.
Bạn cũng có thể dùng Array. Tất nhiên là phải quy định kiểu rồi và sau đây là một vài điểm đáng lưu tâm:
* Kích thước Array cố định
* Không thể dùng các hàm push(), pop(), splice() vân vân và cũng không có thuộc tính
`length` luôn nha
* Không thể gán giá trị hàng loạt khi khởi tạo mà phải gán giá trị từng phần tử mộtCode như sau sẽ không đúng:
glslint values[3] = [0,0,0];
phải như thế này cơ:
glslint values[3];
values[0] = 0;
values[1] = 0;
values[2] = 0;
Điều này cũng không quá tệ nếu bạn thật sự cần can thiệp từng phần tử trong array.
Còn nếu muốn đa dạng hơn thì có thể dùng kiểu `struct`. Chúng giống như các _object_ nhưng không có hàm đi kèm ;
chúng chỉ cho phép đóng gói các biến vào bên trong thôi
glslstruct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
sau đó bạn có thể sử dụng như sau:
glsl// Khởi tạo cấu trúc với giá trị nào đó
ColorStruct sandy = ColorStruct( vec3(0.92,0.83,0.60),
vec3(1.,0.94,0.69),
vec3(0.95,0.86,0.69) );// Truy cập biến trong cấu trúc
sandy.color0 // vec3(0.92,0.83,0.60)
Cú pháp kiểu này có thể hơi tự do chút, nhưng nó sẽ giúp bạn viết code rõ ràng hơn hay ít nhất là nhìn dễ hiểu hơn.#### Các lệnh điều khiển
Cấu trúc dữ liệu hay thật đấy nhưng tới lúc nào đó ta vẫn _có thể_ phải cần tới các lệnh điều khiển.
May mán là cú pháp rất giống với JavaScript.
Ví dụ về lệnh điều kiện:
glslif( condition ){
//true
}else{
//false
}
Còn một vòng lặp thì thường giống như:
glslconst int count = 10;
for( int i = 0; i <= count; i++){
// làm gì đó
}
hoặc với iterator:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
// làm gì đó
}
Chú ý là biến `count` phải được khai báo là một `hằng số (constant)`.
Điều đó có nghĩa là ta phải đặt thêm từ khoá `const` vào trước như ví dụ dưới đây.Ngoài ra ta cũng có các lệnh
`break` và `continue`:
glslconst float count = 10.;
for( float i = 0.0; i <= count; i+= 1.0 ){
if( i < 5. )continue;
if( i >= 8. )break;
}
Chú ý là trên một vài nền tảng phần cứng, lệnh `break` không hoạt động giống nhau nên vòng lặp vẫn chạy.Nhìn chung thì bạn nên giữ số vòng lặp càng ít càng tốt và tránh sử dụng các lệnh điều kiện càng nhiều càng tốt.
#### qualifiers
Không chỉ có kiểu dữ liệu mà GLSL còn dùng qualifier để giúp trình biên dịch biết mỗi biến có gì đặc biệt.
Ví dụ có những dữ liệu chỉ được truyền từ CPU sang GPU gọi là attribute và uniform.
Từ khoá attribute được dùng trong vertex shader, còn uniform được dùng cho cả vertex shader và fragment shader.
Từ khoá
`varying` để đánh dấu các biến luân chuyển giữa vertex shader và fragment shader.Tôi sẽ không đi vào chi tiết ở đây vì ta chủ yếu tập trung vào fragment shader nhưng ở phần sau của quyển sách, bạn sẽ thấy code tương tự như:
glsluniform vec2 u_resolution;
Tôi mới đặt từ khoá `uniform` vào trước kiểu dữ liệu của biến khi khai báo đó.
Điều này có nghĩa là CPU sẽ gửi thêm thông tin cho shader thông qua biến này. Cụ thể đó là độ phân giải của canvas, chiều rộng và chiều cao của canvas sẽ được lưu vào biến x và y của một vector 2 chiều.Khi trình biên dịch thấy một biến có đánh dấu qualifier này, nó sẽ đảm bảo bạn không thể thay đổi giá trị trong shader.
Điều tương tự cũng được áp dụng cho biến
`count` được dùng để giới hạn số vòng lặp `for`:
glslconst float count = 10.;
for( ... )
Khi ta dùng qualifier `const`, trình biên dịch sẽ đảm bảo rằng giá trị của biến này chỉ được khởi tạo một lần duy nhất, nếu không thì nó không phải là hằng số nữa rồi.Còn 3 qualifier hay được dùng nữa cho các tham số của hàm là :
`in`, `out` và `inout`.
Trong JavaScript, khi bạn truyền giá trị vào 1 hàm thì mọi thay đổi với giá trị đó chỉ có tác dụng trong hàm chứ không ảnh hưởng gì tới biến bên ngoài.
glslfunction banana( a ){
a += 1;
}
var value = 0;
banana( value );
console.log( value );// > 0 ; ra khỏi hàm thì value vẫn giữ nguyên giá trị như trước khi gọi hàm
Ý nghĩa của 3 qualifier tham số:
*
`in` không thay đổi giá trị của biến bên ngoài (mặc định cho mọi tham số)
* `out` chỉ dùng để lưu giá trị mới mà vẫn giữ nguyên khi đã ra khỏi hàm
* `inout` tuỳ ý đọc ghiViết lại hàm banana trong GLSL:
glslvoid banana( inout float a ){
a += 1.;
}
float A = 0.;
banana( A ); // lúc này A = 1.;
Điều này rất khác so với JavaScript và cũng rất tiện.#### Không gian và toạ độ
Chú ý cuối cùng, trong DOM và Canvas 2D, trục Y hướng xuống dưới.
Điều này có lý trong bối cảnh DOM dựng trang web có thể scroll được ; từ trên xuống dưới.
Còn trong canvas của WebGL thì trục Y hướng lên trên.
Điều đó có nghĩa là gốc tọa độ, điểm (0, 0) nằm ở góc dưới cùng bên trái của WebGL canvas, chứ không phải góc trên cùng bên trái như 2D Canvas.
Toạ độ của texture cũng vì thế mà có thể gây lú một chút nếu chưa quen.
Và thế là hết !
Tất nhiên ta có thể đi sâu hơn vào rất nhiều khái niệm phía trên, nhưng nhớ rằng đây chỉ là lời chào dành cho người mới.
Đúng là có rất nhiều thứ phải tìm hiểu nhưng với sự kiên trì và chăm chỉ thì sẽ quen nhanh thôi.Tôi hy vọng bạn thấy một vài phần hữu ích ở đây, giờ thì bạn sẵn sàng bắt đầu chuyển phiêu lưu vào quyển sách chưa ?
---
Examples/README (examples/README.md)
Examples Gallery
<p class="gallery_author">Created by <a href="https://www.kynd.info">kynd</a>(<a href="https://twitter.com/kyndinfo">@kyndinfo</a>) and Patricio Gonzalez Vivo(<a href="https://twitter.com/patriciogv">@patriciogv</a>)</p>
This is a collection of examples extracted from the chapters of this book together with shared shaders kindly donated by other readers using the on-line editor. Feel free to explore and tweak them bit by bit. Once you have something you are proud of, click the "Export" and then copy the "URL to code...". Send it to @bookofshaders or @kyndinfo. We are looking forward to see it!
---
Examples/Featured Examples (examples/featured_examples.md)
<div class="glslGallery" data="160423150559,160423150752,160423150810,160423150825,160423150844,160423150901,160423150919,160423150935,160423150954"></div>
---
Examples/README De (examples/README-de.md)
Galerie mit ausgewählten Shader Beispielen
<p class="gallery_author">Kuratiert von <a href="https://www.kynd.info">kynd</a>(<a href="https://twitter.com/kyndinfo">@kyndinfo</a>) und Patricio Gonzalez Vivo(<a href="https://twitter.com/patriciogv">@patriciogv</a>)</p>
Dies ist eine Sammlung von Shader Beispielen aus den verschiedenen Kapiteln dieses Buches. Sie wird ergänzt durch weitere Shader, die von Lesern über den Online-Editor zur Verfügung gestellt wurden.
Du bist herzlich eingeladen, die verschiedenen Shader auszuprobieren, zu erweitern und zu verändern. Und natürlich kannst Du auch ganz neue Shader enwickeln.
Wenn Du Deine Arbeiten anderen Lesern zur Verfügung stellen möchtest, klicke auf die „Export“-Schaltfläche im Editor und kopiere anschließend den Inhalt des Feldes „URL to code...“. Sende diese URL an @bookofshaders oder an @kyndinfo. Wir freuen uns auf Deine Ergebnisse.
---
Examples/README Id (examples/README-id.md)
Galeri Contoh
<p class="gallery_author">Dibuat oleh <a href="https://www.kynd.info">kynd</a>(<a href="https://twitter.com/kyndinfo">@kyndinfo</a>) dan Patricio Gonzalez Vivo(<a href="https://twitter.com/patriciogv">@patriciogv</a>)</p>
Ini adalah kumpulan contoh yang diekstrak dari bab-bab buku ini bersama dengan shader bersama yang disumbangkan oleh pembaca lain menggunakan editor on-line. Jangan ragu untuk menjelajahi dan menyesuaikannya sedikit demi sedikit. Setelah Anda memiliki sesuatu yang Anda banggakan, klik "Export" dan kemudian salin "URL ke kode ...". Kirimkan ke @bookofshaders atau @kyndinfo. Kami sangat menantikan untuk melihatnya!
---
Examples/README Pl (examples/README-pl.md)
Galeria przykładów
<p class="gallery_author">Stworzone przez <a href="https://www.kynd.info">kynd</a>(<a href="https://x.com/kyndinfo">@kyndinfo</a>) oraz Patricio Gonzalez Vivo (<a href="https://x.com/patriciogv">@patriciogv</a>)</p>
To jest zbiór przykładów wyciągniętych z rozdziałów tej książki, wraz z udostępnionymi shaderami hojnie przekazanymi przez innych czytelników korzystających z edytora online. Zachęcamy do eksplorowania i modyfikowania ich krok po kroku. Gdy stworzysz coś, z czego będziesz dumny, kliknij "Export", a następnie skopiuj "URL do kodu...". Prześlij go do @bookofshaders lub @kyndinfo. Nie możemy się doczekać, aby to zobaczyć!
---
Examples/README Ua (examples/README-ua.md)
Галерея прикладів
<p class="gallery_author">Створено за участі by <a href="https://www.kynd.info">kynd</a>(<a href="https://twitter.com/kyndinfo">@kyndinfo</a>) та Patricio Gonzalez Vivo(<a href="https://twitter.com/patriciogv">@patriciogv</a>)</p>
Це колекція прикладів, взятих із розділів цієї книги разом із шейдерами, люб'язно наданими іншими читачами за допомогою онлайн-редактора. Не соромтеся досліджувати та змінювати їх. Якщо у вас вийде приклад, яким ви пишаєтеся, зробіть експорт своїх роботи та надішліть його на адресу @bookofshaders або @kyndinfo. Ми з нетерпінням чекатимемо!
---
Examples/README Vi (examples/README-vi.md)
Thư viện các ví dụ
<p class="gallery_author">Tuyển chọn bởi <a href="https://www.kynd.info">kynd</a>(<a href="https://twitter.com/kyndinfo">@kyndinfo</a>) và Patricio Gonzalez Vivo(<a href="https://twitter.com/patriciogv">@patriciogv</a>)</p>
Đây là một bộ sưu tập các ví dụ được trích từ các chương của quyển sách kèm theo các shader được những độ giả khác đóng góp trên editor online. Bạn có thể tự do khám phá và sửa code tuỳ ý. Nếu bạn thấy ưng ý với bất kỳ tác phẩm nào của mình, bạn có thể click nút "Export" và copy "URL to code...". Sau đó gửi tới địa chỉ @bookofshaders hoặc @kyndinfo. Chúng tôi rất mong chờ được bổ sung các tác phẩm của bạn vào thư viện.
---
Glossary/README (glossary/README.md)
Glossary
By theme
* TYPES
void
bool
int
float
bvec2
bvec3
bvec4
ivec2
ivec3
ivec4
vec2
vec3
vec4
mat2
mat3
mat4
sampler2D
samplerCube
struct
* QUALIFIERS
attribute
const
uniform
varying
precision
highp
mediump
lowp
in
out
inout
* BUILT-IN VARIABLES
gl_Position
gl_PointSize
gl_PointCoord
gl_FrontFacing
gl_FragCoord
gl_FragColor
* BUILT-IN CONSTANTS
gl_MaxVertexAttribs
gl_MaxVaryingVectors
gl_MaxVertexTextureImageUnits
gl_MaxCombinedTextureImageUnits
gl_MaxTextureImageUnits
gl_MaxFragmentUniformVectors
gl_MaxDrawBuffers
* ANGLE & TRIGONOMETRY FUNCTIONS
radians()
degrees()
sin()
cos()
tan()
asin()
acos()
atan()
* EXPONENTIAL FUNCTIONS
pow()
exp()
log()
exp2()
log2()
sqrt()
inversesqrt()
* COMMON FUNCTIONS
abs()
sign()
floor()
ceil()
fract()
mod()
min()
max()
clamp()
mix()
step()
smoothstep()
* GEOMETRIC FUNCTIONS
length()
distance()
dot()
cross()
normalize()
faceforward()
reflect()
refract()
* MATRIX FUNCTIONS
* VECTOR RELATIONAL FUNCTIONS
lessThan()
lessThanEqual()
greaterThan()
greaterThanEqual()
equal()
notEqual()
any()
all()
not()
* TEXTURE LOOKUP FUNCTIONS
Alphabetical
* A
abs()
acos()
all()
any()
asin()
atan()
attribute
* B
* C
ceil()
clamp()
const
cos()
cross()
* D
degrees()
dFdx()
dFdy()
distance()
dot()
* E
* F
faceforward()
float
floor()
fract()
* G
greaterThan()
greaterThanEqual()
gl_FragColor
gl_FragCoord
gl_FrontFacing
gl_PointCoord
gl_PointSize
gl_Position
gl_MaxCombinedTextureImageUnits
gl_MaxDrawBuffers
gl_MaxFragmentUniformVectors
gl_MaxVaryingVectors
gl_MaxVertexAttribs
gl_MaxVertexTextureImageUnits
gl_MaxTextureImageUnits
* H
* I
in
inout
int
inversesqrt()
ivec2
ivec3
ivec4
* L
length()
lessThan()
lessThanEqual()
log()
log2()
lowp
* M
matrixCompMult()
mat2
mat3
mat4
max()
mediump
min()
mix()
mod()
* N
* O
* P
* R
* S
sampler2D
samplerCube
sign()
sin()
smoothstep()
sqrt()
step()
struct
* T
* U
* V
---
Glossary/README Tr (glossary/README-tr.md)
Sözlük
Konuya göre
* TİPLER
void
bool
int
float
bvec2
bvec3
bvec4
ivec2
ivec3
ivec4
vec2
vec3
vec4
mat2
mat3
mat4
sampler2D
samplerCube
struct
* NİTELEYİCİLER
attribute
const
uniform
varying
precision
highp
mediump
lowp
in
out
inout
* YERLEŞİK DEĞİŞKENLER
gl_Position
gl_PointSize
gl_PointCoord
gl_FrontFacing
gl_FragCoord
gl_FragColor
* YERLEŞİK SABİTLER
gl_MaxVertexAttribs
gl_MaxVaryingVectors
gl_MaxVertexTextureImageUnits
gl_MaxCombinedTextureImageUnits
gl_MaxTextureImageUnits
gl_MaxFragmentUniformVectors
gl_MaxDrawBuffers
* AÇI VE TRİGONOMETRİ FONKSİYONLARI
radians()
degrees()
sin()
cos()
tan()
asin()
acos()
atan()
* ÜSTEL FONKSİYONLAR
pow()
exp()
log()
exp2()
log2()
sqrt()
inversesqrt()
* ORTAK FONKSİYONLAR
abs()
sign()
floor()
ceil()
fract()
mod()
min()
max()
clamp()
mix()
step()
smoothstep()
* GEOMETRİK FONKSİYONLAR
length()
distance()
dot()
cross()
normalize()
faceforward()
reflect()
refract()
* MATRİS FONKSİYONLARI
* VEKTÖR İLİŞKİSEL FONKSİYONLARI
lessThan()
lessThanEqual()
greaterThan()
greaterThanEqual()
equal()
notEqual()
any()
all()
not()
* DOKU ARAMA FONKSİYONLARI
Alfabetik
* A
abs()
acos()
all()
any()
asin()
atan()
attribute
* B
* C
ceil()
clamp()
const
cos()
cross()
* D
degrees()
dFdx()
dFdy()
distance()
dot()
* E
* F
faceforward()
float
floor()
fract()
* G
greaterThan()
greaterThanEqual()
gl_FragColor
gl_FragCoord
gl_FrontFacing
gl_PointCoord
gl_PointSize
gl_Position
gl_MaxCombinedTextureImageUnits
gl_MaxDrawBuffers
gl_MaxFragmentUniformVectors
gl_MaxVaryingVectors
gl_MaxVertexAttribs
gl_MaxVertexTextureImageUnits
gl_MaxTextureImageUnits
* H
* I
in
inout
int
inversesqrt()
ivec2
ivec3
ivec4
* L
length()
lessThan()
lessThanEqual()
log()
log2()
lowp
* M
matrixCompMult()
mat2
mat3
mat4
max()
mediump
min()
mix()
mod()
* N
* O
* P
* R
* S
sampler2D
samplerCube
sign()
sin()
smoothstep()
sqrt()
step()
struct
* T
* U
* V
---
Glossary/README Ua (glossary/README-ua.md)
Глосарій
За темою
* ТИПИ
void
bool
int
float
bvec2
bvec3
bvec4
ivec2
ivec3
ivec4
vec2
vec3
vec4
mat2
mat3
mat4
sampler2D
samplerCube
struct
* КВАЛІФІКАТОРИ
attribute
const
uniform
varying
precision
highp
mediump
lowp
in
out
inout
* ВБУДОВАНІ ЗМІННІ
gl_Position
gl_PointSize
gl_PointCoord
gl_FrontFacing
gl_FragCoord
gl_FragColor
* ВБУДОВАНІ КОНСТАНТИ
gl_MaxVertexAttribs
gl_MaxVaryingVectors
gl_MaxVertexTextureImageUnits
gl_MaxCombinedTextureImageUnits
gl_MaxTextureImageUnits
gl_MaxFragmentUniformVectors
gl_MaxDrawBuffers
* ТРИГОНОМЕТРИЧНІ ТА КУТОВІ ФУНКЦІЇ
radians()
degrees()
sin()
cos()
tan()
asin()
acos()
atan()
* ЕКСПОНЕНЦІАЛЬНІ ФУНКЦІЇ
pow()
exp()
log()
exp2()
log2()
sqrt()
inversesqrt()
* ЗАГАЛЬНІ ФУНКЦІЇ
abs()
sign()
floor()
ceil()
fract()
mod()
min()
max()
clamp()
mix()
step()
smoothstep()
* ГЕОМЕТРИЧНІ ФУНКЦІЇ
length()
distance()
dot()
cross()
normalize()
facefoward()
reflect()
refract()
* МАТРИЧНІ ФУНКЦІЇ
* ФУНКЦІЇ ПОВ'ЯЗАНІ З ВЕКТОРАМИ
lessThan()
lessThanEqual()
greaterThan()
greaterThanEqual()
equal()
notEqual()
any()
all()
not()
* ФУНКЦІЇ ПОВ'ЯЗАНІ З ТЕКСТУРАМИ
За алфавітом
* A
abs()
acos()
all()
any()
asin()
atan()
attribute
* B
* C
ceil()
clamp()
const
cos()
cross()
* D
degrees()
dFdx()
dFdy()
distance()
dot()
* E
* F
faceforward()
float
floor()
fract()
* G
greaterThan()
greaterThanEqual()
gl_FragColor
gl_FragCoord
gl_FrontFacing
gl_PointCoord
gl_PointSize
gl_Position
gl_MaxCombinedTextureImageUnits
gl_MaxDrawBuffers
gl_MaxFragmentUniformVectors
gl_MaxVaryingVectors
gl_MaxVertexAttribs
gl_MaxVertexTextureImageUnits
gl_MaxTextureImageUnits
* H
* I
in
inout
int
inversesqrt()
ivec2
ivec3
ivec4
* L
length()
lessThan()
lessThanEqual()
log()
log2()
lowp
* M
matrixCompMult()
mat2
mat3
mat4
max()
mediump
min()
mix()
mod()
* N
* O
* P
* R
radians()
reflect()
refract()
return
* S
sampler2D
samplerCube
sign()
sin()
smoothstep()
sqrt()
step()
struct
* T
* U
* V
---
Glossary/README Vi (glossary/README-vi.md)
Chú giải
Xếp theo nhóm
* CÁC KIỂU DỮ LIỆU
void,
bool,
int,
float,
bvec2,
bvec3,
bvec4,
ivec2,
ivec3,
ivec4,
vec2,
vec3,
vec4,
mat2,
mat3,
mat4,
sampler2D,
samplerCube,
struct
* CÁC QUALIFIER
attribute,
const,
uniform,
varying,
precision,
highp,
mediump,
lowp,
in,
out,
inout
* CÁC BIẾN CÓ SẴN
gl_Position,
gl_PointSize,
gl_PointCoord,
gl_FrontFacing,
gl_FragCoord,
gl_FragColor
* CÁC HẰNG SỐ CÓ SẴN
gl_MaxVertexAttribs,
gl_MaxVaryingVectors,
gl_MaxVertexTextureImageUnits,
gl_MaxCombinedTextureImageUnits,
gl_MaxTextureImageUnits,
gl_MaxFragmentUniformVectors,
gl_MaxDrawBuffers
* CÁC HÀM LƯỢNG GIÁC
radians(),
degrees(),
sin(),
cos(),
tan(),
asin(),
acos(),
atan()
* CÁC HÀM LUỸ THỪA
pow(),
exp(),
log(),
exp2(),
log2(),
sqrt(),
inversesqrt()
* CÁC HÀM TOÁN HỌC CƠ BẢN
abs(),
sign(),
floor(),
ceil(),
fract(),
mod(),
min(),
max(),
clamp(),
mix(),
step(),
smoothstep()
* CÁC HÀM HÌNH HỌC
length(),
distance(),
dot(),
cross(),
normalize(),
facefoward(),
reflect(),
refract()
* CÁC HÀM MA TRẬN
* CÁC HÀM VECTOR
lessThan(),
lessThanEqual(),
greaterThan(),
greaterThanEqual(),
equal(),
notEqual(),
any(),
all(),
not()
* CÁC HÀM TEXTURE
Xếp theo thứ tự từ điển
* A
abs(),
acos(),
all(),
any(),
asin(),
atan(),
attribute
* B
* C
ceil(),
clamp(),
const,
cos(),
cross()
* D
degrees(),
dFdx(),
dFdy(),
distance(),
dot()
* E
faceforward(),
float,
floor(),
fract()
* G
greaterThan(),
greaterThanEqual(),
gl_FragColor,
gl_FragCoord,
gl_FrontFacing,
gl_PointCoord,
gl_PointSize,
gl_Position,
gl_MaxCombinedTextureImageUnits,
gl_MaxDrawBuffers,
gl_MaxFragmentUniformVectors,
gl_MaxVaryingVectors,
gl_MaxVertexAttribs,
gl_MaxVertexTextureImageUnits,
gl_MaxTextureImageUnits
* H
highp
* I
in,
inout,
int,
inversesqrt(),
ivec2,
ivec3,
ivec4
* L
length(),
lessThan(),
lessThanEqual(),
log(),
log2(),
lowp
* M
matrixCompMult(),
mat2,
mat3,
mat4,
max(),
mediump,
min(),
mix(),
mod()
* N
normalize(),
not(),
notEqual()
* O
out
* P
radians(),
reflect(),
refract()
* S
sampler2D,
samplerCube,
sign(),
sin(),
smoothstep(),
sqrt(),
step(),
struct
* T
tan(),
texture2D(),
textureCube()
* U
uniform
* V
varying,
vec2,
vec3,
vec4,
void
---
Glossary/Abs/README (glossary/abs/README.md)
Abs
Return the absolute value of the parameter.Declaration
glslfloat abs(float x)
vec2 abs(vec2 x)
vec3 abs(vec3 x)
vec4 abs(vec4 x)
Parameters
`x` specifies the value of which to return the absolute.Description
`abs()` returns the absolute value of `x`.<div class="simpleFunction" data="y = abs(x); "></div>
See Also
sign(), min(), max(), Chapter 05: Shaping Functions---
Glossary/Abs/README Pl (glossary/abs/README-pl.md)
abs
Zwraca wartość bezwzględną z parametru.Deklaracja
glslfloat abs(float x)
vec2 abs(vec2 x)
vec3 abs(vec3 x)
vec4 abs(vec4 x)
Parametry
`x` określa wartość, z której ma zostać zwrócona wartość bezwzględna.Opis
`abs()` zwraca wartość bezwzględną z x.<div class="simpleFunction" data="y = abs(x); "></div>
Zobacz też
sign(), min(), max(), Rozdział 05: Shaping Functions---
Glossary/Abs/README Ua (glossary/abs/README-ua.md)
abs
Повертає абсолютне значення параметра.Оголошення
glslfloat abs(float x)
vec2 abs(vec2 x)
vec3 abs(vec3 x)
vec4 abs(vec4 x)
Параметри
`x` — значення, яке потрібно повернути в абсолют.Опис
`abs()` повертає абсолютне значення x.<div class="simpleFunction" data="y = abs(x);"></div>
Дивіться також
sign(), min(), max(), Розділ 05: Формотворчі функції---
Glossary/Abs/README Vi (glossary/abs/README-vi.md)
Abs
Tính giá trị tuyệt đối của một hoặc nhiều số.Các phiên bản
glslfloat abs(float x)
vec2 abs(vec2 x)
vec3 abs(vec3 x)
vec4 abs(vec4 x)
Các tham số
`x` số muốn tính giá trị tuyệt đốiMô tả
`abs()` trả về giá trị tuyệt đối của `x`.<div class="simpleFunction" data="y = abs(x); "></div>
Tham khảo thêm
sign(), min(), max(), Chương 5: Các hàm số cơ bản---
Glossary/Acos/README (glossary/acos/README.md)
Acos
Return the arccosine of the parameterDeclaration
glslfloat acos(float x)
vec2 acos(vec2 x)
vec3 acos(vec3 x)
vec4 acos(vec4 x)
Parameters
`x` specifies the value whose arccosine to return.Description
`acos()` returns the angle whose trigonometric cosine is `x`.<div class="simpleFunction" data="y = acos(x); "></div>
See Also
cos(), sin(), asin(), tan(), atan(), Chapter 05: Shaping Functions---
Glossary/Acos/README Pl (glossary/acos/README-pl.md)
acos
Zwraca arcus cosinus podanego argumentuDeklaracja
glslfloat acos(float x)
vec2 acos(vec2 x)
vec3 acos(vec3 x)
vec4 acos(vec4 x)
Parametry
`x` wartość, której arcus cosinus ma zostać zwrócony.Opis
`acos()` zwraca kąt, którego cosinus jest równy `x`.<div class="simpleFunction" data="y = acos(x); "></div>
Zobacz też
cos(), sin(), asin(), tan(), atan(), Rozdział 05: Shaping Functions---
Glossary/Acos/README Ua (glossary/acos/README-ua.md)
acos
Повертає арккосинус параметраОголошення
glslfloat acos(float x)
vec2 acos(vec2 x)
vec3 acos(vec3 x)
vec4 acos(vec4 x)
Параметри
`x` — значення, арккосинус якого потрібно повернути.Опис
`acos()` повертає кут, тригонометричний косинус якого дорівнює x.<div class="simpleFunction" data="y = acos(x);"></div>
Дивіться також
cos(), sin(), asin(), tan(), atan(), Розділ 05: Формотворчі функції---
Glossary/Acos/README Vi (glossary/acos/README-vi.md)
Acos
Tính góc lượng giác ứng với giá trị cosineCác phiên bản
glslfloat acos(float x)
vec2 acos(vec2 x)
vec3 acos(vec3 x)
vec4 acos(vec4 x)
Các tham số
`x` giá trị cosine cần tìm góc lượng giác tương ứngMô tả
`acos()` góc lượng giác (đơn vị radian) ứng với cosine bằng `x`.<div class="simpleFunction" data="y = acos(x); "></div>
Tham khảo thêm
cos(), sin(), asin(), tan(), atan(), Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/All/README (glossary/all/README.md)
All
Check whether all elements of a boolean vector are trueDeclaration
glslbool any(bvec2 x)
bool any(bvec3 x)
bool any(bvec4 x)
Parameters
`x` specifies the vector to be tested for truth.Description
`all()` returns true if all elements of `x` are `true` and `false` otherwise. It is functionally equivalent to:glslbool all(bvec x){ // bvec can be bvec2, bvec3 or bvec4
bool result = true;
int i;
for (i = 0; i < x.length(); ++i)
{
result &= x[i];
}
return result;
}
See Also
any(), not()---
Glossary/All/README Pl (glossary/all/README-pl.md)
all
Sprawdza, czy wszystkie elementy wektora logicznego są prawdziweDeklaracja
glslbool all(bvec2 x)
bool all(bvec3 x)
bool all(bvec4 x)
Parametry
`x` określa wektor, który ma zostać sprawdzony pod kątem prawdy.Opis
`all()` zwraca `true`, jeśli wszystkie elementy `x` są `true` i `false` w przeciwnym razie. Jest to funkcjonalnie równoważne:glslbool all(bvec x){ // bvec może być bvec2, bvec3 lub bvec4
bool result = true;
int i;
for (i = 0; i < x.length(); ++i)
{
result &= x[i];
}
return result;
}
Zobacz też
any(), not()---
Glossary/All/README Ua (glossary/all/README-ua.md)
all
Перевіряє чи всі елементи логічного вектора істинніОголошення
glslbool any(bvec2 x)
bool any(bvec3 x)
bool any(bvec4 x)
Параметри
`x` — вектор, який буде перевірено на істинність.Опис
`all()` повертає true, якщо всі елементи x мають значення true, інакше повертається false. Функціонально це еквівалентно до наступного коду:glslbool all(bvec x) { // bvec може бути bvec2, bvec3 або bvec4
bool result = true;
int i; for (i = 0; i < x.length(); ++i) {
result &= x[i];
}
return result;
}
Дивіться також
any(), not()---
Glossary/All/README Vi (glossary/all/README-vi.md)
All
Kiểm tra xem toàn bộ các thành phần trong 1 vector boolean có cùng bằng true khôngCác phiên bản
glslbool any(bvec2 x)
bool any(bvec3 x)
bool any(bvec4 x)
Các tham số
`x` vector boolean cần kiểm traMô tả
`all()` trả về `true` nếu tất cả các thành phần trong vector `x` đều bằng `true` và trả về `false` nếu ngược lại. Tương đương với:glslbool all(bvec x){ // bvec có thể thay bằng bvec2, bvec3 or bvec4
bool result = true;
int i;
for (i = 0; i < x.length(); ++i)
{
result &= x[i];
}
return result;
}
Tham khảo thêm
any(), not()---
Glossary/Any/README (glossary/any/README.md)
Any
Check whether any element of a boolean vector is trueDeclaration
glslbool all(bvec2 x)
bool all(bvec3 x)
bool all(bvec4 x)
Parameters
`x` specifies the vector to be tested for truth.Description
`any()` returns true if any element of `x` is `true` and `false` otherwise. It is functionally equivalent to:glslbool any(bvec x) { // bvec can be bvec2, bvec3 or bvec4
bool result = false;
int i;
for (i = 0; i < x.length(); ++i) {
result |= x[i];
}
return result;
}
See Also
any(), not()---
Glossary/Any/README Pl (glossary/any/README-pl.md)
any
Sprawdza, czy choć jeden element wektora logicznego jest prawdziwyDeklaracja
glslbool all(bvec2 x)
bool all(bvec3 x)
bool all(bvec4 x)
Parametry
`x` określa wektor, który ma zostać sprawdzony pod kątem prawdy.Opis
`all()` zwraca `true`, jeśli którykolwiek element `x` jest `true` i `false` w przeciwnym razie. Jest to funkcjonalnie równoważne:glslbool any(bvec x) { // bvec może być bvec2, bvec3 lub bvec4
bool result = false;
int i;
for (i = 0; i < x.length(); ++i) {
result |= x[i];
}
return result;
}
Zobacz też
any(), not()---
Glossary/Any/README Ua (glossary/any/README-ua.md)
any
Перевіряє чи хоча б один елемент булевого вектора має значення trueОголошення
glslbool all(bvec2 x)
bool all(bvec3 x)
bool all(bvec4 x)
Параметри
`x` — вектор, який буде перевірено на істинність.Опис
`any()` повертає true, якщо будь-який елемент x має значення true, інакше повертається false. Функціонально це еквівалентно до наступного коду:glslbool any(bvec x) { // bvec може бути bvec2, bvec3 або bvec4
bool result = false;
int i; for (i = 0; i < x.length(); ++i) {
result |= x[i];
}
return result;
}
Дивіться також
any(), not()---
Glossary/Any/README Vi (glossary/any/README-vi.md)
Any
Kiểm tra xem có giá trị nào trong 1 vector kiểu boolean bằng true khôngCác phiên bản
glslbool all(bvec2 x)
bool all(bvec3 x)
bool all(bvec4 x)
Các tham số
`x` vector cần kiểm traMô tả
`any()` trả về `true` nếu có bất kỳ một giá trị nào trong vector `x` bằng `true`, ngược lại trả về `false`. Hàm này tương đương với:glslbool any(bvec x) { // bvec can be bvec2, bvec3 or bvec4
bool result = false;
int i;
for (i = 0; i < x.length(); ++i) {
result |= x[i];
}
return result;
}
Tham khảo thêm
any(), not()---
Glossary/Asin/README (glossary/asin/README.md)
Asin
Return the arcsine of the parameterDeclaration
glslfloat asin(float x)
vec2 asin(vec2 x)
vec3 asin(vec3 x)
vec4 asin(vec4 x)
Parameters
`x` specifies the value whose arcsine to return.Description
`asin()` returns the angle whose trigonometric sine is `x`.<div class="simpleFunction" data="y = asin(x); "></div>
See Also
cos, sin, acos, tan, atan, Chapter 05: Shaping Functions---
Glossary/Asin/README Pl (glossary/asin/README-pl.md)
asin
Zwraca arcsin parametruDeklaracja
glslfloat asin(float x)
vec2 asin(vec2 x)
vec3 asin(vec3 x)
vec4 asin(vec4 x)
Parametry
`x` określa wartość, której arcsin ma zostać zwrócony.Opis
`asin()` zwraca kąt, którego sinus trygonometryczny to `x`.<div class="simpleFunction" data="y = asin(x); "></div>
Zobacz też
cos, sin, acos, tan, atan, Rozdział 05: Shaping Functions---
Glossary/Asin/README Ua (glossary/asin/README-ua.md)
asin
Повертає арксинус параметраОголошення
glslfloat asin(float x)
vec2 asin(vec2 x)
vec3 asin(vec3 x)
vec4 asin(vec4 x)
Параметри
`x` — значення, арксинус якого потрібно повернути.Опис
`asin()` повертає кут, тригонометричний синус якого дорівнює x<div class="simpleFunction" data="y = asin(x);"></div>
Дивіться також
cos, sin, acos, tan, atan, Розділ 05: Формотворчі функції---
Glossary/Asin/README Vi (glossary/asin/README-vi.md)
Asin
Tính góc lượng giác ứng với giá trị sineCác phiên bản
glslfloat asin(float x)
vec2 asin(vec2 x)
vec3 asin(vec3 x)
vec4 asin(vec4 x)
Các tham số
`x` giá trị sine cần tìm góc lượng giác tương ứngMô tả
`asin()` trả về góc lượng giác (đơn vị radian) tương ứng với giá trị sine bằng `x`.<div class="simpleFunction" data="y = asin(x); "></div>
Tham khảo thêm
cos, sin, acos, tan, atan, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Atan/README (glossary/atan/README.md)
Atan
Return the arc-tangent of the parametersDeclaration
glslfloat atan(float y, float x)
vec2 atan(vec2 y, vec2 x)
vec3 atan(vec3 y, vec3 x)
vec4 atan(vec4 y, vec4 x)float atan(float y_over_x)
vec2 atan(vec2 y_over_x)
vec3 atan(vec3 y_over_x)
vec4 atan(vec4 y_over_x)
Parameters
`y` specifies the numerator of the fraction whose arctangent to return.`x` specifies the denominator of the fraction whose arctangent to return.`y_over_x` specifies the fraction whose arctangent to return.Description
`atan()` returns the angle whose trigonometric arctangent is `y,x` or `y_over_x`, depending on which overload is invoked. In the first overload, the signs of `y` and `x` are used to determine the quadrant that the angle lies in. The values returned by `atan` in this case are in the range -PI and PI. Results are undefined if `x` is zero.For the second overload,
`atan()` returns the angle whose tangent is `y_over_x`. Values returned in this case are in the range -PI/2 to PI/2.See Also
cos, acos, sin, asin, atan, Chapter 05: Shaping Functions, Chapter 06: Color---
Glossary/Atan/README Pl (glossary/atan/README-pl.md)
atan
Zwraca arcus tangens parametrówDeklaracja
glslfloat atan(float y, float x)
vec2 atan(vec2 y, vec2 x)
vec3 atan(vec3 y, vec3 x)
vec4 atan(vec4 y, vec4 x)float atan(float y_over_x)
vec2 atan(vec2 y_over_x)
vec3 atan(vec3 y_over_x)
vec4 atan(vec4 y_over_x)
Parametry
`y` określa licznik ułamka, którego arcus tangens ma zostać zwrócony.`x` określa mianownik ułamka, którego arcus tangens ma zostać zwrócony.`y_over_x` określa ułamek, którego arcus tangens ma zostać zwrócony.Opis
`atan()` zwraca kąt, którego trygonometryczny arcus tangens jest równy `y,x` lub `y_over_x`, w zależności od tego, który przeciążenie jest wywoływane. W pierwszym przeciążeniu znaki `y` i `x` są używane do określenia ćwiartki, w której znajduje się kąt. Wartości zwracane przez `atan` w tym przypadku mieszczą się w zakresie -PI i PI. Wyniki są niezdefiniowane, jeśli `x` wynosi zero.Dla drugiego przeciążenia,
`atan()` zwraca kąt, którego tangens wynosi `y_over_x`. Wartości zwracane w tym przypadku mieszczą się w zakresie -PI do PI.Zobacz też
cos, acos, sin, asin, atan, Rozdział 05: Shaping Functions, Rozdział 06: Kolory
[]: # (end)
[]: # (end)---
Glossary/Atan/README Ua (glossary/atan/README-ua.md)
atan
Повертає арктангенс параметрівОголошення
glslfloat atan(float y, float x)
vec2 atan(vec2 y, vec2 x)
vec3 atan(vec3 y, vec3 x)
vec4 atan(vec4 y, vec4 x)float atan(float y_over_x)
vec2 atan(vec2 y_over_x)
vec3 atan(vec3 y_over_x)
vec4 atan(vec4 y_over_x)
Параметри
`y` — чисельник дробу, арктангенс якого потрібно повернути.
`x` — знаменник дробу, арктангенс якого потрібно повернути.
`y_over_x` — дріб, арктангенс якого потрібно повернути.Опис
`atan()` повертає кут, тригонометричний арктангенс якого дорівнює y,x або y_over_x, залежно від того, яке саме перевантаження функції викликається.У першому перевантаженні знаки
y і x використовуються для визначення квадранта, в якому лежить кут. Значення, які повертає atan у цьому випадку, знаходяться в діапазоні від -PI до PI. Результати не визначені, якщо x дорівнює нулю.Для другого перевантаження
`atan()` повертає кут, тангенс якого дорівнює `y_over_x`. Значення, що повертаються в цьому випадку, знаходяться в діапазоні від -PI до PI.Дивіться також
cos, acos, sin, asin, atan, Розділ 05: Формотворчі функції, Розділ 06: Кольори---
Glossary/Attribute/README (glossary/attribute/README.md)
Attribute
Vertex attribute data.Example
glslattribute vec4 v_color;
Description
`attribute` read-only variables containing data shared from WebGL/OpenGL environment to the vertex shader.Because the vertex shader is executed one time for each vertex, attributes are specify per vertex data typically with information such as: space position, color, normal direction and texture coordinates of a vertex.
See Also
const, uniform, varying, Chapter 03: Uniforms---
Glossary/Attribute/README Pl (glossary/attribute/README-pl.md)
attribute
Dane atrybutów wierzchołka.Przykład
glslattribute vec4 v_color;
Opis
attribute to zmienne tylko do odczytu, zawierające dane udostępniane z środowiska WebGL/OpenGL do vertex shadera.Ponieważ vertex shader jest uruchamiany raz dla każdego wierzchołka, atrybuty określają dane specyficzne dla poszczególnych wierzchołków, takie jak: pozycja w przestrzeni, kolor, kierunek wektora normalnego czy współrzędne tekstury.
Zobacz też
const, uniform, varying, Rozdział 03: Uniformy---
Glossary/Attribute/README Ua (glossary/attribute/README-ua.md)
attribute
Дані атрибутів вершин.Приклад
glslattribute vec4 v_color;
Опис
`attribute` — змінні доступні лише для читання, що містять дані, які передаються із середовища WebGL/OpenGL у вершинний шейдер.Оскільки вершинний шейдер виконується один раз для кожної вершини, атрибути визначають дані для кожної вершини, як правило, з такою інформацією, як: положення в просторі, колір, напрямок нормалі та координати текстури вершини.
Дивіться також
const, uniform, varying, Розділ 03: Uniforms---
Glossary/Attribute/README Vi (glossary/attribute/README-vi.md)
Attribute
Đánh dấu dữ liệu về các đỉnh của mesh.Ví dụ
glslattribute vec4 v_color;
Mô tả
`attribute` đánh dấu dữ liệu về các đỉnh của mesh gửi từ môi trường WebGL/OpenGL cho vertex shader.Vì vertex shader chỉ thực thi một lần cho mỗi đỉnh, nên các thuộc tính của mỗi đỉnh phải được chỉ đích danh: vị trí, màu sắc, vector pháp tuyến và toạ độ UV.
Tham khảo thêm
const, uniform, varying, Chương 03: Uniform---
Glossary/Bool/README (glossary/bool/README.md)
Bool
Boolean variable typeDeclaration
glslbool aBool = true;
bool bBool = bool(aInt);
bool cBool = bool(aFloat);
Description
`bool` data type is either true or false.See Also
void, bool, int, float, bvec2, bvec3, bvec4, struct---
Glossary/Bool/README Pl (glossary/bool/README-pl.md)
bool
Typ zmiennej logicznejDeklaracja
glslbool aBool = true;
bool bBool = bool(aInt);
bool cBool = bool(aFloat);
Opis
bool może przyjmować dwie wartości: true lub false.Zobacz też
void, bool, int, float, bvec2, bvec3, bvec4, struct---
Glossary/Bool/README Ua (glossary/bool/README-ua.md)
bool
Логічний тип даних (також булів, булевий, булівський)Приклад
glslbool aBool = true;
bool bBool = bool(aInt);
bool cBool = bool(aFloat);
Опис
`bool` — використовується для позначення логічного типу даних. Приймає значення true або false. Також є одноіменна функція для приведення даних до відповідного типу. Дивіться також
void, bool, int, float, bvec2, bvec3, bvec4, struct---
Glossary/Bool/README Vi (glossary/bool/README-vi.md)
Bool
Kiểu dữ liệu booleanCác phiên bản
glslbool aBool = true;
bool bBool = bool(aInt);
bool cBool = bool(aFloat);
Mô tả
`bool` chỉ nhận 1 trong 2 giá trị đúng hoặc saiTham khảo thêm
void, bool, int, float, bvec2, bvec3, bvec4, struct---
Glossary/Bvec2/README (glossary/bvec2/README.md)
Bvec2
2 dimensional boolean vectorDeclaration
glslbvec2 aBvec2 = bvec2(true, true);
bvec2 bBvec2 = bvec2(true);bvec2 cBvec2 = bvec2(aBvec3);
bvec2 dBvec2 = bvec2(aBvec3.x, aBvec3.y);
Description
`bvec2` is a boolean vector with two components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a vector of higher dimension. The respective values are used to initialize the components.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec2/README Pl (glossary/bvec2/README-pl.md)
bvec2
2-wymiarowy wektor boolowskiDeklaracja
glslbvec2 aBvec2 = bvec2(true, true);
bvec2 bBvec2 = bvec2(true);bvec2 cBvec2 = bvec2(aBvec3);
bvec2 dBvec2 = bvec2(aBvec3.x, aBvec3.y);
Opis
bvec2 to wektor boolowski z dwoma komponentami. Można go zainicjalizować:- Podając wartość skalarną dla każdego komponentu.
- Podając jedną wartośś skalarną. Zostanie ona przypisana do wszystkich komponentów.
- Podając wektor o wyższym wymiarze. Odpowiednie wartości zostaną użyte do inicjalizacji komponentów.
Zobacz też
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec2/README Ua (glossary/bvec2/README-ua.md)
bvec2
2-вимірний булевий векторОголошення
glslbvec2 aBvec2 = bvec2(true, true);
bvec2 bBvec2 = bvec2(true);bvec2 cBvec2 = bvec2(aBvec3);
bvec2 dBvec2 = bvec2(aBvec3.x, aBvec3.y);
Опис
`bvec2` — булевий вектор з двома компонентами. Способи ініціалізації:- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання вектора вищої розмірності, де відповідні значення будуть використані для ініціалізації компонентів;
Дивіться також
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec2/README Vi (glossary/bvec2/README-vi.md)
Bvec2
Vector boolean 2 chiềuCác phiên bản
glslbvec2 aBvec2 = bvec2(true, true);
bvec2 bBvec2 = bvec2(true);bvec2 cBvec2 = bvec2(aBvec3);
bvec2 dBvec2 = bvec2(aBvec3.x, aBvec3.y);
Mô tả
`bvec2` vector boolean gồm 2 thành phần. Nó có thể được khởi tạo bằng các cách:
- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 2 thành phần
- Lấy giá trị từ 1 vector nhiều chiều hơn. Các thành phần sẽ được khởi tạo theo thứ tự.Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec3/README (glossary/bvec3/README.md)
Bvec3
3 dimensional boolean vectorDeclaration
glslvec3 aBvec3 = bvec3(true, true, true);
vec3 bBvec3 = bvec3(true);vec3 cBvec3 = bvec3(aBvec4);
vec3 dBvec3 = bvec3(aBvec4.x, aBvec4.y, aBvec4.z);
vec3 eBvec3 = bvec3(aBvec2, aBool);
vec3 fBvec3 = bvec3(aBvec2.x, aBvec2.y, aBool);
Description
`bvec3` is a boolean vector with three components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a vector of higher dimension. The respective values are used to initialize the components.
- Providing a combination of vectors and/or scalars. The respective values are used to initialize the vector. The arguments of the constructor must have at least as many components as the vector that is initialized.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec3/README Pl (glossary/bvec3/README-pl.md)
bvec3
3-wymiarowy wektor boolowskiDeklaracja
glslvec3 aBvec3 = bvec3(true, true, true);
vec3 bBvec3 = bvec3(true);vec3 cBvec3 = bvec3(aBvec4);
vec3 dBvec3 = bvec3(aBvec4.x, aBvec4.y, aBvec4.z);
vec3 eBvec3 = bvec3(aBvec2, aBool);
vec3 fBvec3 = bvec3(aBvec2.x, aBvec2.y, aBool);
Opis
bvec3 to wektor boolowski z trzema komponentami. Można go zainicjalizować:- Podając wartość skalarną dla każdego komponentu.
- Podając jedną wartość skalarną – zostanie ona przypisana do wszystkich komponentów.
- Podając wektor o wyższym wymiarze – odpowiednie wartości zostaną użyte do inicjalizacji komponentów.
- Podając kombinację wektorów i/lub skalarów – odpowiednie wartości zostaną użyte do inicjalizacji wektora. Argumenty konstruktora muszą zawierać co najmniej tyle komponentów, ile ma inicjalizowany wektor.
Zobacz też
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec3/README Vi (glossary/bvec3/README-vi.md)
Bvec3
Vector boolean 3 chiềuCác phiên bản
glslvec3 aBvec3 = bvec3(true, true, true);
vec3 bBvec3 = bvec3(true);vec3 cBvec3 = bvec3(aBvec4);
vec3 dBvec3 = bvec3(aBvec4.x, aBvec4.y, aBvec4.z);
vec3 eBvec3 = bvec3(aBvec2, aBool);
vec3 fBvec3 = bvec3(aBvec2.x, aBvec2.y, aBool);
Mô tả
`bvec3` vector boolean gồm 3 thành phần. Nó có thể được khởi tạo bằng các cách:
- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 3 thành phần
- Lấy giá trị từ 1 vector nhiều chiều hơn. Các thành phần sẽ được khởi tạo theo thứ tự.
- Lấy giá trị theo thứ tự lần lượt từ nhiều vector, miễn là đủ 3 thành phần.Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec4/README (glossary/bvec4/README.md)
Bvec4
4 dimensional boolean vectorDeclaration
glslvec4 aBvec4 = bvec4(true, true, true, true);
vec4 bBvec4 = bvec4(true);vec4 cBvec4 = bvec4(aBvec2, aBool, aBvec3);
vec4 dBvec4 = bvec4(aBvec2.x, aBvec2.y, aBool, aBvec3.x);
Description
`bvec4` is a boolean vector with four components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a combination of vectors and scalars. The respective values are used to initialize the components. The arguments of the constructor must have at least as many components as the vector that is initialized.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec4/README Pl (glossary/bvec4/README-pl.md)
bvec4
4-wymiarowy wektor boolowskiDeklaracja
glslvec4 aBvec4 = bvec4(true, true, true, true);
vec4 bBvec4 = bvec4(true);vec4 cBvec4 = bvec4(aBvec2, aBool, aBvec3);
vec4 dBvec4 = bvec4(aBvec2.x, aBvec2.y, aBool, aBvec3.x);
Opis
bvec4 to wektor boolowski mający cztery składowe. Można go zainicjalizować:- Podając osobną wartość skalarną dla każdej składowej.
- Podając jedną wartość skalarną. Wartość ta zostanie użyta dla wszystkich składowych.
- Podając kombinację wektorów i skalarów. Odpowiednie wartości zostaną użyte do inicjalizacji składowych. Argumenty konstruktora muszą mieć przynajmniej tyle składowych, ile wynosi rozmiar inicjalizowanego wektora.
Zobacz też
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec4/README Ua (glossary/bvec4/README-ua.md)
bvec4
4-вимірний булевий векторОголошення
glslvec4 aBvec4 = bvec4(true, true, true, true);
vec4 bBvec4 = bvec4(true);vec4 cBvec4 = bvec4(aBvec2, aBool, aBvec3);
vec4 dBvec4 = bvec4(aBvec2.x, aBvec2.y, aBool, aBvec3.x);
Опис
`bvec4` — булевий вектор з чотирма компонентами. Способи ініціалізації:- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання комбінації векторів та/або скалярів. Для ініціалізації вектора використовуються відповідні значення. Аргументи конструктора повинні містити принаймні стільки ж компонентів, скільки ініціалізований вектор.
Дивіться також
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Bvec4/README Vi (glossary/bvec4/README-vi.md)
Bvec4
Vector boolean 4 chiềuCác phiên bản
glslvec4 aBvec4 = bvec4(true, true, true, true);
vec4 bBvec4 = bvec4(true);vec4 cBvec4 = bvec4(aBvec2, aBool, aBvec3);
vec4 dBvec4 = bvec4(aBvec2.x, aBvec2.y, aBool, aBvec3.x);
Mô tả
`bvec4` vector boolean gồm 4 thành phần. Nó có thể được khởi tạo bằng các cách:
- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 4 thành phần
- Lấy giá trị theo thứ tự lần lượt từ nhiều vector, miễn là đủ 4 thành phần.Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ceil/README (glossary/ceil/README.md)
Ceil
Find the nearest integer that is greater than or equal to the parameterDeclaration
glslfloat ceil(float x)
vec2 ceil(vec2 x)
vec3 ceil(vec3 x)
vec4 ceil(vec4 x)
Parameters
`x` specifies the value to evaluate.Description
`ceil()` returns a value equal to the nearest integer that is greater than or equal to `x`.<div class="simpleFunction" data="y = ceil(x); "></div>
See Also
floor, fract, mod, Chapter 05: Shaping Functions---
Glossary/Ceil/README Pl (glossary/ceil/README-pl.md)
ceil
Znajduje najbliższą liczbę całkowitą, która jest większa lub równa podanemu parametrowiDeklaracja
glslfloat ceil(float x)
vec2 ceil(vec2 x)
vec3 ceil(vec3 x)
vec4 ceil(vec4 x)
Parametry
`x` określa wartość do ewaluacjiOpis
`ceil()` zwraca wartość równą najbliższej liczbie całkowitej, która jest większa bądź równa `x`.<div class="simpleFunction" data="y = ceil(x);"></div>
Zobacz też
floor, fract, mod, Rozdział 05: Shaping Functions---
Glossary/Ceil/README Ua (glossary/ceil/README-ua.md)
ceil
Знаходить і повертає найближче ціле число, яке більше або дорівнює параметруОголошення
glslfloat ceil(float x)
vec2 ceil(vec2 x)
vec3 ceil(vec3 x)
vec4 ceil(vec4 x)
Параметри
`x` — значення для обробки.Опис
`ceil()` повертає значення, що дорівнює найближчому цілому числу, яке більше або дорівнює x.<div class="simpleFunction" data="y = ceil(x);"></div>
Дивіться також
floor, fract, mod, Розділ 05: Формотворчі функції---
Glossary/Ceil/README Vi (glossary/ceil/README-vi.md)
Ceil
Tìm số nguyên lớn hơn hoặc bằng gần nhất (làm tròn lên).Các phiên bản
glslfloat ceil(float x)
vec2 ceil(vec2 x)
vec3 ceil(vec3 x)
vec4 ceil(vec4 x)
Các tham số
`x` giá trị cần làm trònMô tả
`ceil()` làm tròn `x` thành số lớn hơn hoặc bằng.<div class="simpleFunction" data="y = ceil(x); "></div>
Tham khảo thêm
floor, fract, mod, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Clamp/README (glossary/clamp/README.md)
Clamp
Constrain a value to lie between two further valuesDeclaration
glslfloat clamp(float x, float minVal, float maxVal)
vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal)
vec3 clamp(vec3 x, vec3 minVal, vec3 maxVal)
vec4 clamp(vec4 x, vec4 minVal, vec4 maxVal)vec2 clamp(vec2 x, float minVal, float maxVal)
vec3 clamp(vec3 x, float minVal, float maxVal)
vec4 clamp(vec4 x, float minVal, float maxVal)
Parameters
`x` specifies the value to constrain.`minVal` specifies the lower end of the range into which to constrain x.`maxVal` specifies the upper end of the range into which to constrain x.Description
`clamp()` returns the value of `x` constrained to the range `minVal` to `maxVal`. The returned value is computed as `min(max(x, minVal), maxVal)`.<div class="simpleFunction" data="y = clamp(x,0.,1.); "></div>
See Also
min, abs, max---
Glossary/Clamp/README Pl (glossary/clamp/README-pl.md)
clamp
Ogranicza wartość, aby mieściła się między dwoma skrajnymi wartościamiDeklaracja
glslfloat clamp(float x, float minVal, float maxVal)
vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal)
vec3 clamp(vec3 x, vec3 minVal, vec3 maxVal)
vec4 clamp(vec4 x, vec4 minVal, vec4 maxVal)vec2 clamp(vec2 x, float minVal, float maxVal)
vec3 clamp(vec3 x, float minVal, float maxVal)
vec4 clamp(vec4 x, float minVal, float maxVal)
Parametry
`x` określa wartość, którą należy ograniczyć.`minVal` określa dolną granicę zakresu, w którym będzie ograniczana wartość x.`maxVal` określa górną granicę zakresu, w którym będzie ograniczana wartość x.Opis
`clamp()` zwraca wartość `x` ograniczoną do zakresu od `minVal` do `maxVal`. Zwrócona wartość obliczana jest jako `min(max(x, minVal), maxVal)`.<div class="simpleFunction" data="y = clamp(x,0.,1.);"></div>
Zobacz też
min, abs, max---
Glossary/Clamp/README Ua (glossary/clamp/README-ua.md)
clamp
Повертає значення в діапазоні між двома обмежувачамиОголошення
glslfloat clamp(float x, float minVal, float maxVal)
vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal)
vec3 clamp(vec3 x, vec3 minVal, vec3 maxVal)
vec4 clamp(vec4 x, vec4 minVal, vec4 maxVal)vec2 clamp(vec2 x, float minVal, float maxVal)
vec3 clamp(vec3 x, float minVal, float maxVal)
vec4 clamp(vec4 x, float minVal, float maxVal)
Параметри
`x` — значення для обмеження.
`minVal` — нижня межа діапазону.
`maxVal` — верхня межа діапазону.Опис
`clamp()` повертає значення x обмежене діапазоном від minVal до maxVal. Повернене значення обчислюється як `min(max(x, minVal), maxVal)`.<div class="simpleFunction" data="y = clamp(x,0.,1.);"></div>
Дивіться також
min, abs, max, Розділ 05: Формотворчі функції---
Glossary/Clamp/README Vi (glossary/clamp/README-vi.md)
Clamp
Giới hạn 1 giá trị nằm trong 1 khoảng.Các phiên bản
glslfloat clamp(float x, float minVal, float maxVal)
vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal)
vec3 clamp(vec3 x, vec3 minVal, vec3 maxVal)
vec4 clamp(vec4 x, vec4 minVal, vec4 maxVal)vec2 clamp(vec2 x, float minVal, float maxVal)
vec3 clamp(vec3 x, float minVal, float maxVal)
vec4 clamp(vec4 x, float minVal, float maxVal)
Các tham số
`x` giá trị cần giới hạn.`minVal` giá trị nhỏ nhất trong khoảng giới hạn`maxVal` giá trị lớn nhất trong khoảng giới hạnMô tả
`clamp()` trả về giá trị `x` sau khi đã đảm bảo nó nằm trong khoảng từ `minVal` tới `maxVal`. Cách tính chi tiết: `min(max(x, minVal), maxVal)`.<div class="simpleFunction" data="y = clamp(x,0.,1.); "></div>
Tham khảo thêm
min, abs, max---
Glossary/Const/README (glossary/const/README.md)
Const
Constant qualifierExample
glslconst float PI = 3.14159265359;
Description
`const` qualifier can be applied to the declaration of any variable to specify that its value will not be changed.See also
attribute, uniform, varying---
Glossary/Const/README Pl (glossary/const/README-pl.md)
const
Kwalifikator stałyPrzykład
glslconst float PI = 3.14159265359;
Opis
Kwalifikator `const` może być zastosowany do deklaracji dowolnej zmiennej, aby określić, że jej wartość nie ulegnie zmianie.Zobacz też
attribute, uniform, varying---
Glossary/Const/README Ua (glossary/const/README-ua.md)
const
Константний кваліфікаторПриклад
glslconst float PI = 3.14159265359;
Опис
`const` — кваліфікатор, який застосується перед оголошенням будь-якої змінної, щоб вказати, що значення цієї змінної не буде і не може бути змінено.Дивіться також
attribute, uniform, varying---
Glossary/Const/README Vi (glossary/const/README-vi.md)
Const
Qualifier đánh dấu một hằng sốVí dụ
glslconst float PI = 3.14159265359;
Mô tả
`const` khiến cho giá trị của một biến không thể bị thay đổiTham khảo thêm
attribute, uniform, varying---
Glossary/Cos/README (glossary/cos/README.md)
Cos
Return the cosine of the parameterDeclaration
glslfloat cos(float angle)
vec2 cos(vec2 angle)
vec3 cos(vec3 angle)
vec4 cos(vec4 angle)
Parameters
`angle` specify the quantity, in radians, of which to return the cosine.Description
`cos()` returns the trigonometric cosine of angle.<div class="simpleFunction" data="y = cos(x); "></div>
See Also
acos, sin, asin, tan, atan, Chapter 05: Shaping Functions---
Glossary/Cos/README Pl (glossary/cos/README-pl.md)
cos
Zwraca cosinus podanego parametruDeklaracja
glslfloat cos(float angle)
vec2 cos(vec2 angle)
vec3 cos(vec3 angle)
vec4 cos(vec4 angle)
Parametry
`angle` określa wartość (w radianach), dla której ma zostać zwrócony cosinus.Opis
`cos()` zwraca trygonometryczny cosinus podanego kąta.<div class="simpleFunction" data="y = cos(x); "></div>
Zobacz też
acos, sin, asin, tan, atan, Rozdział 05: Shaping Functions---
Glossary/Cos/README Ua (glossary/cos/README-ua.md)
cos
Повертає косинус параметраОголошення
glslfloat cos(float angle)
vec2 cos(vec2 angle)
vec3 cos(vec3 angle)
vec4 cos(vec4 angle)
Параметри
`angle` — величина в радіанах, косинус якої потрібно повернути.Опис
`cos()` повертає тригонометричний косинус кута.<div class="simpleFunction" data="y = cos(x);"></div>
Дивіться також
acos, sin, asin, tan, atan, Розділ 05: Формотворчі функції---
Glossary/Cos/README Vi (glossary/cos/README-vi.md)
Cos
Tính hàm cosine của 1 góc lượng giácCác phiên bản
glslfloat cos(float angle)
vec2 cos(vec2 angle)
vec3 cos(vec3 angle)
vec4 cos(vec4 angle)
Các tham số
`angle` góc lượng giác (đơn vị radian) cần tính cosineMô tả
`cos()` trả về giá trị cosine tương ứng với góc `angletext<div class="simpleFunction" data="y = cos(x); "></div>Tham khảo thêm
acos, sin, asin, tan, atan, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Cross/README (glossary/cross/README.md)
Cross
Calculate the cross product of two vectorsDeclaration
glsl
vec3 cross(vec3 x, vec3 y)
textParameters
x` specifies the first of two vectors`y` specifies the second of two vectorsDescription
`cross()` returns the cross product of two vectors, `x` and `y`. The input parameters can only be 3-component floating vectors. The cross product is equivalent to the product of the length of the vectors times the sinus of the(smaller) angle between `x` and `y`.See Also
---
Glossary/Cross/README Pl (glossary/cross/README-pl.md)
cross
Oblicza iloczyn wektorowy dwóch wektorówDeklaracja
glslvec3 cross(vec3 x, vec3 y)
Parametry
`x` określa pierwszy z dwóch wektorów`y` określa drugi z dwóch wektorówOpis
`cross()` zwraca iloczyn wektorowy dwóch wektorów, `x` i `y`. Parametry wejściowe mogą być wyłącznie 3-składowymi wektorami zmiennoprzecinkowymi. Iloczyn wektorowy jest równoważny iloczynowi długości tych wektorów pomnożonemu przez sinus (mniejszego) kąta między `x` i `y`.Zobacz też
dot---
Glossary/Cross/README Ua (glossary/cross/README-ua.md)
cross
Обчислює перехресний добуток двох векторівОголошення
glslvec3 cross(vec3 x, vec3 y)
Параметри
`x` — перший вектор.
`y` — другий вектор.Опис
`cross()` повертає перехресний добуток двох векторів, x та y. Вхідними параметрами можуть бути лише 3-компонентні вектори — vec3. Перехресний добуток еквівалентний добутку довжини векторів на синус (меншого) кута між x і y.Дивіться також
dot()---
Glossary/Cross/README Vi (glossary/cross/README-vi.md)
Cross
Tính tích có hướng của 2 vectorCác phiên bản
glslvec3 cross(vec3 x, vec3 y)
Các tham số
`x` Vector thứ nhất`y` Vector thứ haiMô tả
`cross()` trả về tích có hướng của hai vector `x` và `y`. Các tham số đầu vào phải là các vector số thực 3 chiều. Tham khảo thêm
---
Glossary/Degrees/README (glossary/degrees/README.md)
Degrees
Convert a quantity in radians to degreesDeclaration
glslfloat degrees(float radians)
vec2 degrees(vec2 radians)
vec3 degrees(vec3 radians)
vec4 degrees(vec4 radians)
Parameters
`radians` specifies the quantity, in radians, to be converted to degrees.Description
`degrees()` converts a quantity, specified in radians into degrees. That is, the return value is `(180.0*radians)/PItextSee Also
radians---
Glossary/Degrees/README Pl (glossary/degrees/README-pl.md)
degrees
Konwertuje wielkość w radianach na stopnieDeklaracja
glsl
float degrees(float radians)
vec2 degrees(vec2 radians)
vec3 degrees(vec3 radians)
vec4 degrees(vec4 radians)
textParametry
radians` określa wielkość (w radianach), która ma zostać zamieniona na stopnie.Opis
`degrees()` konwertuje wielkość wyrażoną w radianach na stopnie. Oznacza to, że wartość zwracana to `(180.0*radians)/PI`.Zobacz też
radians---
Glossary/Degrees/README Ua (glossary/degrees/README-ua.md)
degrees
Конвертує передане значення у градусиОголошення
glslfloat degrees(float radians)
vec2 degrees(vec2 radians)
vec3 degrees(vec3 radians)
vec4 degrees(vec4 radians)
Параметри
`radians` — значення у радіанах, яке потрібно перетворити в градуси.Опис
`degrees()` перетворює значення, передане в радіанах, у градуси.Обрахунок по формулі:
`(180.0 * radians) / PItextДивіться також
radians---
Glossary/Degrees/README Vi (glossary/degrees/README-vi.md)
Degrees
Chuyển đổi góc từ đơn vị radian sang độCác phiên bản
glsl
float degrees(float radians)
vec2 degrees(vec2 radians)
vec3 degrees(vec3 radians)
vec4 degrees(vec4 radians)
textCác tham số
radians` góc theo đơn vị radianMô tả
`degrees()` trả về góc tính bằng độ tương ứng, theo công thức: `(180.0 * radians)/PItextTham khảo thêm
radians---
Glossary/DFdx/README (glossary/dFdx/README.md)
DFdx
Return the partial derivative of an argument with respect to xDeclaration
glsl
genType dFdx(float x);
textParameters
p` specifies the expression of which to take the partial derivative.Description
Available only in the fragment shader, `dFdx` return the partial derivative of expression `p` in `x`. Derivatives are calculated using local differencing. Expressions that imply higher order derivatives such as `dFdx(dFdx(n))` have undefined results, as do mixed-order derivatives such as `dFdx(dFdy(n))`. It is assumed that the expression `p` is continuous and therefore, expressions evaluated via non-uniform control flow may be undefined.See Also
dFdy()---
Glossary/DFdx/README Pl (glossary/dFdx/README-pl.md)
dFdx
Zwraca pochodną cząstkową podanego wyrażenia względem xDeklaracja
glslgenType dFdx(float x);
Parametry
`p` określa wyrażenie, dla którego chcemy obliczyć pochodną cząstkową.Opis
Dostępna wyłącznie w fragment shaderze , `dFdx` zwraca pochodną cząstkową wyrażenia `p` względem `x`. Pochodne obliczane są poprzez lokalne różnicowanie. Wyrażenia oznaczające pochodne wyższego rzędu, takie jak `dFdx(dFdx(n))`, zwracają niezdefiniowane wyniki, podobnie jak mieszane pochodne, np. `dFdx(dFdy(n))`. Przyjmuje się, że wyrażenie `p` jest ciągłe, więc wyrażenia oceniane w warunkowym przepływie sterowania (non-uniform control flow) mogą być niezdefiniowane.Zobacz też
dFdy---
Glossary/DFdx/README Vi (glossary/dFdx/README-vi.md)
DFdx
Tính đạo hàm riêng của xCác phiên bản
glslgenType dFdx( genType p);
Các tham số
`p` biểu thức cần tính đạo hàm riêngMô tả
Chỉ được dùng trong fragment shader, hàm
`dFdx` sẽ trả về đạo hàm riêng của biểu thức `p` ứng với giá trị`x`. Đạo hàm được tính bằng phương pháp sai phân hữu hạn. Không tính được kết quả của đạo hàm bậc cao như `dFdx(dFdx(n))`. Giả thiết là biểu thức `p` là một hàm liên tục.Tham khảo thêm
dFdy()---
Glossary/DFdy/README (glossary/dFdy/README.md)
DFdy
Return the partial derivative of an argument with respect to yDeclaration
glslgenType dFdy(float y);
Parameters
`p` specifies the expression of which to take the partial derivative.Description
Available only in the fragment shader, `dFdy` return the partial derivative of expression `p` in `y`. Derivatives are calculated using local differencing. Expressions that imply higher order derivatives such as `dFdy(dFdy(n))` have undefined results, as do mixed-order derivatives such as `dFdy(dFdx(n))`. It is assumed that the expression `p` is continuous and therefore, expressions evaluated via non-uniform control flow may be undefined.See Also
dFdx()---
Glossary/DFdy/README Pl (glossary/dFdy/README-pl.md)
dFdy
Zwraca pochodną cząstkową podanego wyrażenia względem yDeklaracja
glslgenType dFdy(float y);
Parametry
`p` określa wyrażenie, dla którego chcemy obliczyć pochodną cząstkową.Opis
Dostępna wyłącznie w fragment shaderze, `dFdy` zwraca pochodną cząstkową wyrażenia `p` względem `y`. Pochodne obliczane są poprzez lokalne różnicowanie. Wyrażenia oznaczające pochodne wyższego rzędu, takie jak `dFdy(dFdy(n))`, zwracają niezdefiniowane wyniki, podobnie jak mieszane pochodne, np. `dFdy(dFdx(n))`. Przyjmuje się, że wyrażenie `p` jest ciągłe, więc wyrażenia oceniane w warunkowym przepływie sterowania (non-uniform control flow) mogą być niezdefiniowane.Zobacz też
dFdx---
Glossary/DFdy/README Vi (glossary/dFdy/README-vi.md)
DFdy
Tính đạo hàm riêng của yCác phiên bản
glslgenType dFdx( genType p);
Các tham số
`p` biểu thức cần tính đạo hàm riêngMô tả
Chỉ được dùng trong fragment shader, hàm
`dFdy` sẽ trả về đạo hàm riêng của biểu thức `p` ứng với giá trị`x`. Đạo hàm được tính bằng phương pháp sai phân hữu hạn. Không tính được kết quả của đạo hàm bậc cao như `dFdy(dFdy(n))`. Giả thiết là biểu thức `p` là một hàm liên tục.Tham khảo thêm
dFdx()---
Glossary/Distance/README (glossary/distance/README.md)
Distance
Calculate the distance between two pointsDeclaration
glslfloat distance(float p0, float p1)
float distance(vec2 p0, vec2 p1)
float distance(vec3 p0, vec3 p1)
float distance(vec4 p0, vec4 p1)
Parameters
`p0` specifies the first of two points`p1` specifies the second of two pointsDescription
`distance()` returns the distance between the two points `p0` and `p1`.<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
See Also
length(), normalize(), Chapter 07: Shapes
---
Glossary/Distance/README Pl (glossary/distance/README-pl.md)
distance
Oblicza odległość między dwoma punktamiDeklaracja
glslfloat distance(float p0, float p1)
float distance(vec2 p0, vec2 p1)
float distance(vec3 p0, vec3 p1)
float distance(vec4 p0, vec4 p1)
Parametry
`p0` określa pierwszy z dwóch punktów`p1` określa drugi z dwóch punktówOpis
`distance()` zwraca odległość pomiędzy dwoma punktami `p0` i `p1`.<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
Zobacz też
length, normalize, Rozdział 07: Kształty---
Glossary/Distance/README Ua (glossary/distance/README-ua.md)
distance
Обчислює відстань між двома точкамиОголошення
glslfloat distance(float p0, float p1)
float distance(vec2 p0, vec2 p1)
float distance(vec3 p0, vec3 p1)
float distance(vec4 p0, vec4 p1)
Параметри
`p0` — перша точка.
`p1` — друга точка.Опис
`distance()` повертає відстань між двома точками p0 і p1.<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
Дивіться також
length(), normalize(), Розділ 07: Фігури---
Glossary/Distance/README Vi (glossary/distance/README-vi.md)
Distance
Tính khoảng cách giữa hai điểmCác phiên bản
glslfloat distance(float p0, float p1)
float distance(vec2 p0, vec2 p1)
float distance(vec3 p0, vec3 p1)
float distance(vec4 p0, vec4 p1)
Các tham số
`p0` Điểm thứ nhất`p1` Điểm thứ haiMô tả
`distance()` trả về khoảng cách giữa hai điểm `p0` và `p1`.<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
Tham khảo thêm
length(), normalize(), Chương 7: Hình dạng
---
Glossary/Dot/README (glossary/dot/README.md)
Dot
Calculate the dot product of two vectorsDeclaration
glslfloat dot(float x, float y)
float dot(vec2 x, vec2 y)
float dot(vec3 x, vec3 y)
float dot(vec4 x, vec4 y)
Parameters
`x` specifies the first of two vectors`y` specifies the second of two vectorsDescription
`dot()` returns the dot product of two vectors, `x` and `y`. i.e., `x[0]⋅y[0]+x[1]⋅y[1]+...textIf
x` and `y` are the same the square root of the dot product is equivalent to the length of the vector. The input parameters can be floating scalars or float vectors. In case of floating scalars the dot function is trivial and returns the product of x and y.<div class="codeAndCanvas" data="../07/circle.frag"></div>
See Also
---
Glossary/Dot/README Pl (glossary/dot/README-pl.md)
dot
Oblicza iloczyn skalarny (dot product) dwóch wektorówDeklaracja
glslfloat dot(float x, float y)
float dot(vec2 x, vec2 y)
float dot(vec3 x, vec3 y)
float dot(vec4 x, vec4 y)
Parametry
`x` określa pierwszy z dwóch wektorów`y` określa drugi z dwóch wektorówOpis
`dot()` zwraca iloczyn skalarny dwóch wektorów, `x` i `y`, tzn. `x[0]⋅y[0] + x[1]⋅y[1] + ...textJeśli
x` i `y` są identyczne, wówczas pierwiastek kwadratowy z iloczynu skalarnego jest równoważny długości wektora. Parametry wejściowe mogą być skalarnymi wartościami zmiennoprzecinkowymi lub wektorami zmiennoprzecinkowymi. W przypadku skalarów obliczenia sprowadzają się do pomnożenia `x` przez `y`.<div class="codeAndCanvas" data="../07/circle.frag"></div>
Zobacz też
cross, Rozdział 07: Kształty---
Glossary/Dot/README Ua (glossary/dot/README-ua.md)
dot
Обчислює скалярний добуток двох векторівОголошення
glslfloat dot(float x, float y)
float dot(vec2 x, vec2 y)
float dot(vec3 x, vec3 y)
float dot(vec4 x, vec4 y)
Параметри
`x` — перший вектор.
`y` — другий вектор.Опис
`dot()` повертає скалярний добуток двох векторів, x та y. Тобто: "`x[0] y[0] + x[1] y[1] +...`"
Якщо x і y однакові, квадратний корінь із скалярного добутку еквівалентний довжині вектора. Вхідними параметрами можуть бути скаляри з типом float або відповідні вектори. У випадку скалярів функція `dot()` є тривіальною та повертає добуток x та y.<div class="codeAndCanvas" data="../07/circle.frag"></div>
Дивіться також
cross(), Розділ 07: Фігури---
Glossary/Dot/README Vi (glossary/dot/README-vi.md)
Dot
Tính tích vô hướng của hai vectorCác phiên bản
glslfloat dot(float x, float y)
float dot(vec2 x, vec2 y)
float dot(vec3 x, vec3 y)
float dot(vec4 x, vec4 y)
Các tham số
`x` Vector thứ nhất`y` Vector thứ haiMô tả
`dot()` trả về tích vô hướng của hai vector `x` và `y` theo công thức `x[0]⋅y[0]+x[1]⋅y[1]+...textNếu
x` và `y` giống nhau thì căn bậc hai của tích vô hướng sẽ bằng đúng chiều dài của vector.
are the same the square root of the dot product is equivalent to the length of the vector. The input parameters can be floating scalars or float vectors. In case of floating scalars the dot function is trivial and returns the product of x and y.
<div class="codeAndCanvas" data="../07/circle.frag"></div>
Tham khảo thêm
---
Glossary/Equal/README (glossary/equal/README.md)
Equal
Perform a component-wise equal-to comparison of two vectorsDeclaration
glslbvec2 equal(vec2 x, vec2 y)
bvec3 equal(vec3 x, vec3 y)
bvec4 equal(vec4 x, vec4 y) bvec2 equal(ivec2 x, ivec2 y)
bvec3 equal(ivec3 x, ivec3 y)
bvec4 equal(ivec4 x, ivec4 y)
Parameters
`x` specifies the first vector to be used in the comparison operation.`y` specifies the second vector to be used in the comparison operation.Description
`equal()` returns a boolean vector in which each element `i` is computed as `x[i] == y[i]`.See Also
lessThanEqual(), lessThan(), greaterThanEqual(), greaterThan(), notEqual(), any(), all(), not()---
Glossary/Equal/README Ua (glossary/equal/README-ua.md)
equal
Виконує по-компонентне порівняння двох векторівОголошення
glslbvec2 equal(vec2 x, vec2 y)
bvec3 equal(vec3 x, vec3 y)
bvec4 equal(vec4 x, vec4 y) bvec2 equal(ivec2 x, ivec2 y)
bvec3 equal(ivec3 x, ivec3 y)
bvec4 equal(ivec4 x, ivec4 y)
Параметри
`x` — перший вектор для порівняння.
`y` — другий вектор для порівняння.Опис
`equal()` повертає булів вектор, у якому кожен елемент i обчислюється як "`x[i] == y[i]`".Дивіться також
lessThanEqual(), lessThan(), greaterThanEqual(), greaterThan(), notEqual(), any(), all(), not()---
Glossary/Equal/README Vi (glossary/equal/README-vi.md)
Equal
Thực hiện phép so sánh bằng giữa các cặp giá trị của 2 vector.Các phiên bản
glslbvec2 equal(vec2 x, vec2 y)
bvec3 equal(vec3 x, vec3 y)
bvec4 equal(vec4 x, vec4 y) bvec2 equal(ivec2 x, ivec2 y)
bvec3 equal(ivec3 x, ivec3 y)
bvec4 equal(ivec4 x, ivec4 y)
Các tham số
`x` Vector thứ nhất.`y` Vector thứ hai.Mô tả
`equal()` trả về một vector boolean mà thành phần thứ `i` là kết quả của phép so sánh `x[i] == y[i]`.Tham khảo thêm
lessThanEqual(), lessThan(), greaterThanEqual(), greaterThan(), notEqual(), any(), all(), not()---
Glossary/Exp/README (glossary/exp/README.md)
Exp
Return the natural exponentiation of the parameterDeclaration
glslfloat exp(float x)
vec2 exp(vec2 x)
vec3 exp(vec3 x)
vec4 exp(vec4 x)
Parameters
`x` specifies the value to exponentiate.Description
`exp()` returns the natural exponentiation of `x`.<div class="simpleFunction" data="y = exp(x); "></div>
See Also
log, log2, exp2, Chapter 05: Shaping Functions
---
Glossary/Exp/README Ua (glossary/exp/README-ua.md)
exp
Повертає експоненту параметра — число Ейлера піднесене до заданого степеня.
Число Ейлера або e приблизно дорівнює 2.718281828Оголошення
glslfloat exp(float x)
vec2 exp(vec2 x)
vec3 exp(vec3 x)
vec4 exp(vec4 x)
Параметри
`x` — степінь до якого потрібно піднести число Ейлера.Опис
`exp()` повертає число e піднесене до степеня x.<div class="simpleFunction" data="y = exp(x); "></div>
Дивіться також
log, log2, exp2, Розділ 05: Формотворчі функції---
Glossary/Exp/README Vi (glossary/exp/README-vi.md)
Exp
Tính lũy thừa tự nhiên của một sốCác phiên bản
glslfloat exp(float x)
vec2 exp(vec2 x)
vec3 exp(vec3 x)
vec4 exp(vec4 x)
Các tham số
`x` số cần tính lũy thừa tự nhiên.Mô tả
`exp()` trả về kết quả của phép tính e mũ `x` (e là cơ số của logarith tự nhiên, xấp xỉ 2.71828)<div class="simpleFunction" data="y = exp(x); "></div>
Tham khảo thêm
log, log2, exp2, Các hàm số cơ bản (Hàm hình dạng - Shape function)
---
Glossary/Exp2/README (glossary/exp2/README.md)
Exp2
Return 2 raised to the power of the parameterDeclaration
glslfloat exp2(float x)
vec2 exp2(vec2 x)
vec3 exp2(vec3 x)
vec4 exp2(vec4 x)
Parameters
`x` specifies the value of the power to which 2 will be raised.Description
`exp2()` returns 2 raised to the power of `x`.<div class="simpleFunction" data="y = exp2(x); "></div>
See Also
log, log2, exp, Chapter 05: Shaping Functions
---
Glossary/Exp2/README Ua (glossary/exp2/README-ua.md)
exp2
Повертає число 2 піднесене до заданого степеня.Оголошення
glslfloat exp2(float x)
vec2 exp2(vec2 x)
vec3 exp2(vec3 x)
vec4 exp2(vec4 x)
Параметри
`x` — значення степеня, до якого буде піднесено число 2.Опис
`exp2()` повертає число 2 у степені x.<div class="simpleFunction" data="y = exp2(x); "></div>
Дивіться також
log, log2, exp, Розділ 05: Формотворчі функції---
Glossary/Exp2/README Vi (glossary/exp2/README-vi.md)
Exp2
Hàm 2 mũ x.Các phiên bản
glslfloat exp2(float x)
vec2 exp2(vec2 x)
vec3 exp2(vec3 x)
vec4 exp2(vec4 x)
Các tham số
`x` số mũ.Mô tả
`exp2()` trả về kết quả của phép tính 2 mũ `x`.<div class="simpleFunction" data="y = exp2(x); "></div>
Tham khảo thêm
log, log2, exp, Các hàm số cơ bản (Hàm hình dạng - Shape function)
---
Glossary/Faceforward/README (glossary/faceforward/README.md)
Faceforward
Return a vector pointing in the same direction as anotherDeclaration
glslfloat faceforward(float N, float I, float Nref)
vec2 faceforward(vec2 N, vec2 I, vec2 Nref)
vec3 faceforward(vec3 N, vec3 I, vec3 Nref)
vec4 faceforward(vec4 N, vec4 I, vec4 Nref)
Parameters
`N` specifies the vector to orient.`I` specifies the incident vector.`Nref` specifies the reference vector.Description
`faceforward()` orients a vector to point away from a surface as defined by its normal. `If dot(Nref, I) < 0` faceforward returns `N`, otherwise it returns `-N`.See Also
reflect(), refract()---
Glossary/Faceforward/README Ua (glossary/faceforward/README-ua.md)
faceforward
Повертає вектор, що вказує в тому ж напрямку, що й іншийОголошення
glslfloat faceforward(float N, float I, float Nref)
vec2 faceforward(vec2 N, vec2 I, vec2 Nref)
vec3 faceforward(vec3 N, vec3 I, vec3 Nref)
vec4 faceforward(vec4 N, vec4 I, vec4 Nref)
Параметри
`N` — вектор для орієнтації.
`I` — вектор інциденту.
`Nref` — опорний вектор.Опис
`faceforward()` орієнтує вектор так, щоб він був направлений від поверхні, яка визначена її нормаллю. Якщо `If dot(Nref, I) < 0` функція повертає N, інакше -N.Дивіться також
reflect(), refract()---
Glossary/Faceforward/README Vi (glossary/faceforward/README-vi.md)
Faceforward
Tìm vector pháp tuyến cùng phía với vector tới so với một bề mặt.Các phiên bản
glslfloat faceforward(float N, float I, float Nref)
vec2 faceforward(vec2 N, vec2 I, vec2 Nref)
vec3 faceforward(vec3 N, vec3 I, vec3 Nref)
vec4 faceforward(vec4 N, vec4 I, vec4 Nref)
Các tham số
`N` vector pháp tuyến của bề mặt sẽ va chạm (hướng trả về sẽ trùng hướng này hoặc hướng ngược lại)`I` vector tới (hướng tới vị trí va chạm với bề mặt).`Nref` vector pháp tuyến của bề mặt cần kiểm tra.Mô tả
`faceforward()` trả về hướng của bề mặt (vector pháp tuyến) tại vị trí va chạm với tia tới. Nếu `dot(Nref, I) < 0` trả về `N`, ngược lại trả về `-N`.Tham khảo thêm
reflect(), refract()---
Glossary/Float/README (glossary/float/README.md)
Float
Floating-point variable typeDeclaration
glslfloat aFloat = 1.0;
float bFloat = float(aBool);
float cFloat = float(aInt);
Description
`float` is used for floating point values.See Also
void, bool, int, float, bvec2, bvec3, bvec4, struct---
Glossary/Float/README Ua (glossary/float/README-ua.md)
float
Тип даних для чисел з рухомою крапкоюПриклад
glslfloat aFloat = 1.0;
float bFloat = float(aBool);
float cFloat = float(aInt);
Опис
`float` — використовується для позначення змінних із числами з рухомою крапкою. Також є одноіменна функція для приведення даних до відповідного типу.Дивіться також
void, bool, int, float, vec2, vec3, vec4, struct---
Glossary/Float/README Vi (glossary/float/README-vi.md)
Float
Kiểu dữ liệu số thực.Các phiên bản
glslfloat aFloat = 1.0;
float bFloat = float(aBool);
float cFloat = float(aInt);
Mô tả
`float` lưu giá trị của số thực, bao gồm cả phần nguyên lẫn phần thập phân.Tham khảo thêm
void, bool, int, float, bvec2, bvec3, bvec4, struct---
Glossary/Floor/README (glossary/floor/README.md)
Floor
Find the nearest integer less than or equal to the parameterDeclaration
glslfloat floor(float x)
vec2 floor(vec2 x)
vec3 floor(vec3 x)
vec4 floor(vec4 x)
Parameters
`x` specifies the value to evaluate.Description
`floor()` returns a value equal to the nearest integer that is less than or equal to `x`.<div class="simpleFunction" data="y = floor(x); "></div>
See Also
ceil, fract, mod, Chapter 05: Shaping Functions---
Glossary/Floor/README Ua (glossary/floor/README-ua.md)
floor
Знаходить і повертає найближче ціле число, яке менше або дорівнює параметруОголошення
glslfloat floor(float x)
vec2 floor(vec2 x)
vec3 floor(vec3 x)
vec4 floor(vec4 x)
Параметри
`x` — значення для обробки.Опис
`floor()` повертає значення, що дорівнює найближчому цілому числу, яке менше або дорівнює x.<div class="simpleFunction" data="y = floor(x);"></div>
Дивіться також
ceil, fract, mod, Розділ 05: Формотворчі функції---
Glossary/Floor/README Vi (glossary/floor/README-vi.md)
Floor
Tìm số nguyên gần nhất nhỏ hơn hoặc bằng (Làm tròn xuống).Các phiên bản
glslfloat floor(float x)
vec2 floor(vec2 x)
vec3 floor(vec3 x)
vec4 floor(vec4 x)
Các tham số
`x` số cần làm tròn xuốngMô tả
`floor()` trả về số nguyên gần nhất nhỏ hơn hoặc bằng với `x`.<div class="simpleFunction" data="y = floor(x); "></div>
Tham khảo thêm
ceil, fract, mod, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Fract/README (glossary/fract/README.md)
Fract
Compute the fractional part of the argumentDeclaration
glslfloat fract(float x)
vec2 fract(vec2 x)
vec3 fract(vec3 x)
vec4 fract(vec4 x)
Parameters
`x` specifies the value to evaluate.Description
`fract()` returns the fractional part of `x`. This is calculated as `x - floor(x)`.<div class="simpleFunction" data="y = fract(x); "></div>
See Also
floor, ceil, mod, Chapter 05: Shaping Functions---
Glossary/Fract/README Ua (glossary/fract/README-ua.md)
fract
Повертає дробову частину аргументуОголошення
glslfloat fract(float x)
vec2 fract(vec2 x)
vec3 fract(vec3 x)
vec4 fract(vec4 x)
Параметри
`x` — значення для обробки.Опис
`fract()` повертає дробову частину від x. Це обчислюється як `x - floor(x)`.<div class="simpleFunction" data="y = fract(x);"></div>
Дивіться також
floor, ceil, mod, Розділ 05: Формотворчі функції---
Glossary/Fract/README Vi (glossary/fract/README-vi.md)
Fract
Tách phần thập phân của số thực.Các phiên bản
glslfloat fract(float x)
vec2 fract(vec2 x)
vec3 fract(vec3 x)
vec4 fract(vec4 x)
Các tham số
`x` số thực cần tách phần thập phân.Mô tả
`fract()` trả về phần thập phân của `x` theo công thức `x - floor(x)`.<div class="simpleFunction" data="y = fract(x); "></div>
Tham khảo thêm
floor, ceil, mod, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/GL ES/README (glossary/GL_ES/README.md)
GL_ES
Declaration / Example
Parameters
Description
See Also
---
Glossary/GL ES/README Vi (glossary/GL_ES/README-vi.md)
GL_ES
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl FragColor/README (glossary/gl_FragColor/README.md)
Gl_FragColor
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl FragColor/README Ua (glossary/gl_FragColor/README-ua.md)
gl_FragColor
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl FragColor/README Vi (glossary/gl_FragColor/README-vi.md)
Gl_FragColor
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl FragCoord/README (glossary/gl_FragCoord/README.md)
Gl_FragCoord
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl FragCoord/README Ua (glossary/gl_FragCoord/README-ua.md)
gl_FragCoord
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl FragCoord/README Vi (glossary/gl_FragCoord/README-vi.md)
G_FragCoord
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl FrontFacing/README (glossary/gl_FrontFacing/README.md)
Gl_FrontFacing
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl FrontFacing/README Ua (glossary/gl_FrontFacing/README-ua.md)
gl_FrontFacing
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl FrontFacing/README Vi (glossary/gl_FrontFacing/README-vi.md)
Gl_FrontFacing
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl MaxCombinedTextureImageUnits/README (glossary/gl_MaxCombinedTextureImageUnits/README.md)
Gl_MaxCombinedTextureImages
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl MaxCombinedTextureImageUnits/README Ua (glossary/gl_MaxCombinedTextureImageUnits/README-ua.md)
gl_MaxCombinedTextureImages
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl MaxCombinedTextureImageUnits/README Vi (glossary/gl_MaxCombinedTextureImageUnits/README-vi.md)
Gl_MaxCombinedTextureImages
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl MaxDrawBuffers/README (glossary/gl_MaxDrawBuffers/README.md)
Gl_MaxDrawBuffers
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl MaxDrawBuffers/README Ua (glossary/gl_MaxDrawBuffers/README-ua.md)
gl_MaxDrawBuffers
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl MaxDrawBuffers/README Vi (glossary/gl_MaxDrawBuffers/README-vi.md)
Gl_MaxDrawBuffers
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl MaxFragmentUniformVectors/README (glossary/gl_MaxFragmentUniformVectors/README.md)
Gl_MaxFragmentUniformVectors
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl MaxFragmentUniformVectors/README Ua (glossary/gl_MaxFragmentUniformVectors/README-ua.md)
gl_MaxFragmentUniformVectors
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl MaxFragmentUniformVectors/README Vi (glossary/gl_MaxFragmentUniformVectors/README-vi.md)
Gl_MaxFragmentUniformVectors
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl MaxTextureImageUnits/README (glossary/gl_MaxTextureImageUnits/README.md)
Gl_MaxTextureImageUnits
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl MaxTextureImageUnits/README Ua (glossary/gl_MaxTextureImageUnits/README-ua.md)
gl_MaxTextureImageUnits
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl MaxTextureImageUnits/README Vi (glossary/gl_MaxTextureImageUnits/README-vi.md)
Gl_MaxTextureImageUnits
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl MaxVaryingVectors/README (glossary/gl_MaxVaryingVectors/README.md)
Gl_MaxVaryingVectors
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl MaxVaryingVectors/README Ua (glossary/gl_MaxVaryingVectors/README-ua.md)
gl_MaxVaryingVectors
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl MaxVaryingVectors/README Vi (glossary/gl_MaxVaryingVectors/README-vi.md)
Gl_MaxVaryingVectors
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl MaxVertexAttribs/README (glossary/gl_MaxVertexAttribs/README.md)
Gl_MaxVertexAttrbis
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl MaxVertexAttribs/README Ua (glossary/gl_MaxVertexAttribs/README-ua.md)
gl_MaxVertexAttribs
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl MaxVertexAttribs/README Vi (glossary/gl_MaxVertexAttribs/README-vi.md)
Gl_MaxVertexAttrbis
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl MaxVertexTextureImageUnits/README (glossary/gl_MaxVertexTextureImageUnits/README.md)
Gl_MaxVertexTextureImageUnits
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl MaxVertexTextureImageUnits/README Ua (glossary/gl_MaxVertexTextureImageUnits/README-ua.md)
gl_MaxVertexTextureImageUnits
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl MaxVertexTextureImageUnits/README Vi (glossary/gl_MaxVertexTextureImageUnits/README-vi.md)
Gl_MaxVertexTextureImageUnits
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl PointCoord/README (glossary/gl_PointCoord/README.md)
Gl_PointCoord
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl PointCoord/README Ua (glossary/gl_PointCoord/README-ua.md)
gl_PointCoord
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl PointCoord/README Vi (glossary/gl_PointCoord/README-vi.md)
Gl_PointCoord
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl PointSize/README (glossary/gl_PointSize/README.md)
Gl_PointSize
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl PointSize/README Ua (glossary/gl_PointSize/README-ua.md)
gl_PointSize
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl PointSize/README Vi (glossary/gl_PointSize/README-vi.md)
Gl_PointSize
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Gl Position/README (glossary/gl_Position/README.md)
Gl_Position
Declaration / Example
Parameters
Description
See Also
---
Glossary/Gl Position/README Ua (glossary/gl_Position/README-ua.md)
gl_Position
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Gl Position/README Vi (glossary/gl_Position/README-vi.md)
Gl_Position
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/GreaterThan/README (glossary/greaterThan/README.md)
GreaterThan
Perform a component-wise greater-than comparison of two vectorsDeclaration
glslbvec2 greaterThan(vec2 x, vec2 y)
bvec3 greaterThan(vec3 x, vec3 y)
bvec4 greaterThan(vec4 x, vec4 y) bvec2 greaterThan(ivec2 x, ivec2 y)
bvec3 greaterThan(ivec3 x, ivec3 y)
bvec4 greaterThan(ivec4 x, ivec4 y)
Parameters
`x` specifies the first vector to be used in the comparison operation.`y` specifies the second vector to be used in the comparison operation.Description
`greaterThan()` returns a boolean vector in which each element `i` is computed as `x[i] > y[i]`.See Also
lessThanEqual(), lessThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/GreaterThan/README Ua (glossary/greaterThan/README-ua.md)
greaterThan
Виконує по-компонентне порівняння двох векторів на більшість компонентів першого вектора по відношенню до другогоОголошення
glslbvec2 greaterThan(vec2 x, vec2 y)
bvec3 greaterThan(vec3 x, vec3 y)
bvec4 greaterThan(vec4 x, vec4 y) bvec2 greaterThan(ivec2 x, ivec2 y)
bvec3 greaterThan(ivec3 x, ivec3 y)
bvec4 greaterThan(ivec4 x, ivec4 y)
Параметри
`x` — перший вектор для порівняння.
`y` — другий вектор для порівняння.Опис
`greaterThan()` повертає булів вектор, у якому кожен елемент i обчислюється як "`x[i] > y[i]`".Дивіться також
lessThanEqual(), lessThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/GreaterThan/README Vi (glossary/greaterThan/README-vi.md)
GreaterThan
Thực hiện phép so sánh lớn hơn với từng cặp giá trị trong 2 vectorCác phiên bản
glslbvec2 greaterThan(vec2 x, vec2 y)
bvec3 greaterThan(vec3 x, vec3 y)
bvec4 greaterThan(vec4 x, vec4 y) bvec2 greaterThan(ivec2 x, ivec2 y)
bvec3 greaterThan(ivec3 x, ivec3 y)
bvec4 greaterThan(ivec4 x, ivec4 y)
Các tham số
`x` Vector thứ nhất`y` Vector thứ haiMô tả
`greaterThan()` trả về một vector boolean mà thành phần thứ `i` là kết quả của phép so sánh `x[i] > y[i]`.Tham khảo thêm
lessThanEqual(), lessThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/GreaterThanEqual/README (glossary/greaterThanEqual/README.md)
GreaterThanEqual
Perform a component-wise greater-than-or-equal comparison of two vectorsDeclaration
glslbvec2 greaterThanEqual(vec2 x, vec2 y)
bvec3 greaterThanEqual(vec3 x, vec3 y)
bvec4 greaterThanEqual(vec4 x, vec4 y) bvec2 greaterThanEqual(ivec2 x, ivec2 y)
bvec3 greaterThanEqual(ivec3 x, ivec3 y)
bvec4 greaterThanEqual(ivec4 x, ivec4 y)
Parameters
`x` specifies the first vector to be used in the comparison operation.`y` specifies the second vector to be used in the comparison operation.Description
`greaterThanEqual()` returns a boolean vector in which each element `i` is computed as `x[i] ≥ y[i]`.See Also
lessThanEqual(), lessThan(), greaterThan(), equal(), notEqual(), any(), all(), not()---
Glossary/GreaterThanEqual/README Ua (glossary/greaterThanEqual/README-ua.md)
greaterThanEqual
Виконує по-компонентне порівняння двох векторів на більшість або рівність компонентів першого вектора по відношенню до другогоОголошення
glslbvec2 greaterThanEqual(vec2 x, vec2 y)
bvec3 greaterThanEqual(vec3 x, vec3 y)
bvec4 greaterThanEqual(vec4 x, vec4 y) bvec2 greaterThanEqual(ivec2 x, ivec2 y)
bvec3 greaterThanEqual(ivec3 x, ivec3 y)
bvec4 greaterThanEqual(ivec4 x, ivec4 y)
Параметри
`x` — перший вектор для порівняння.
`y` — другий вектор для порівняння.Опис
`greaterThanEqual()` повертає булів вектор, у якому кожен елемент i обчислюється як "`x[i] ≥ y[i]`".Дивіться також
lessThanEqual(), lessThan(), greaterThan(), equal(), notEqual(), any(), all(), not()---
Glossary/GreaterThanEqual/README Vi (glossary/greaterThanEqual/README-vi.md)
GreaterThanEqual
Thực hiện phép so sánh lớn-hơn-hoặc-bằng cho từng cặp giá trị giữa hai vector.Các phiên bản
glslbvec2 greaterThanEqual(vec2 x, vec2 y)
bvec3 greaterThanEqual(vec3 x, vec3 y)
bvec4 greaterThanEqual(vec4 x, vec4 y) bvec2 greaterThanEqual(ivec2 x, ivec2 y)
bvec3 greaterThanEqual(ivec3 x, ivec3 y)
bvec4 greaterThanEqual(ivec4 x, ivec4 y)
Các tham số
`x` Vector thứ nhất.`y` Vector thứ hai.Mô tả
`greaterThanEqual()` trả về một vector boolean mà thành phần thứ `i` là kết quả của phép so sánh `x[i] ≥ y[i]`.Tham khảo thêm
lessThanEqual(), lessThan(), greaterThan(), equal(), notEqual(), any(), all(), not()---
Glossary/Highp/README (glossary/highp/README.md)
Highp
Declaration / Example
Parameters
Description
See Also
---
Glossary/Highp/README Ua (glossary/highp/README-ua.md)
highp
Кваліфікатор точності, задається на початку шейдераПриклад
glslprecision highp float;
precision highp int;
Опис
Кваліфікатор точності оголошує мінімальний діапазон і точність, які повинні використовуватися під час зберігання змінних відповідного типу.
`highp` — 32+ біт, діапазон для float від -2^62 to +2^62, діапазон для int від -2^16 до 2^16Дивіться також
lowp, mediump, precision---
Glossary/Highp/README Vi (glossary/highp/README-vi.md)
Highp
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/In/README (glossary/in/README.md)
In
Declaration / Example
Parameters
Description
See Also
---
Glossary/In/README Ua (glossary/in/README-ua.md)
in
Кваліфікатор доступу аргументів. Позначає аргумент лише на читання. Застосовується по замовчуванню.Приклад
glsl// Кваліфікатор "in" можна не прописувати явно, бо він застосовується по замовчуванню
float updateValue(in float x) {
// ці зміни для змінної "х" відбудуться лише усередині функції
x = x + x; // x = (0.5 + 0.5) = 1.0 return x; // 1.0
}
void main() {
float x = 0.5;
float totalSum = updateValue(x); // totalSum == 1.0
// x == 0.5, оскільки параметр був переданий лише на читання, то після виконання функції його значення залишається як і було
}
Опис
`in` — кваліфікатор доступу аргументів, який дозволяє лише читати позначену змінну. Зміни всередині функції не вплинуть на значення переданої змінної поза її межами.Дивіться також
out, inout---
Glossary/In/README Vi (glossary/in/README-vi.md)
In
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Inout/README (glossary/inout/README.md)
Inout
Declaration / Example
Parameters
Description
See Also
---
Glossary/Inout/README Ua (glossary/inout/README-ua.md)
inout
Кваліфікатор доступу аргументів. Задає можливість як читати так і змінювати аргумент. Нове значення змінної буде також доступне й за межами функції де відбулася ця зміна.Приклад
glslvoid increment(inout int x) {
x = x + 1;
}void main() {
int count = 1;
increment(count);
// тепер count == 2, оскільки змінна була передана у функцію із inout кваліфікатором та була оновлена
}
Опис
`inout` — кваліфікатор доступу аргументів, який дозволяє як читати так і змінювати позначену змінну. Зміна значення застосується не лише всередині функції, але і поза її межами, як показано у прикладі.Дивіться також
in, out---
Glossary/Inout/README Vi (glossary/inout/README-vi.md)
Inout
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Int/README (glossary/int/README.md)
Int
Integer data typeDeclaration
glslint aInt = 42;
int bInt = int(aBool);
int cInt = int(aFloat);
Description
`int` is used for integer values.See Also
void, bool, int, float, ivec2, ivec3, ivec4---
Glossary/Int/README Ua (glossary/int/README-ua.md)
int
Цілочисельний тип данихПриклад
glslint aInt = 42;
int bInt = int(aBool);
int cInt = int(aFloat);
Опис
`int` — використовується для позначення змінних із цілочисельними значеннями. Також є одноіменна функція для приведення даних до відповідного типу.Дивіться також
void, bool, int, float, ivec2, ivec3, ivec4---
Glossary/Int/README Vi (glossary/int/README-vi.md)
Int
Kiểu dữ liệu số nguyên.Các phiên bản
glslint aInt = 42;
int bInt = int(aBool);
int cInt = int(aFloat);
Mô tả
`int` lưu giá trị của một số nguyên.Tham khảo thêm
void, bool, int, float, ivec2, ivec3, ivec4---
Glossary/Inversesqrt/README (glossary/inversesqrt/README.md)
Inversesqrt
Return the inverse of the square root of the parameterDeclaration
glslfloat inversesqrt(float x)
vec2 inversesqrt(vec2 x)
vec3 inversesqrt(vec3 x)
vec4 inversesqrt(vec4 x)
Parameters
`x` specifies the value of which to take the inverse of the square root.Description
`inversesqrt()` returns the inverse of the square root of `x`.<div class="simpleFunction" data="y = inversesqrt(x); "></div>
See Also
pow, sqrt, Chapter 05: Shaping Functions
---
Glossary/Inversesqrt/README Ua (glossary/inversesqrt/README-ua.md)
inversesqrt
Повертає значення, обернене квадратному кореню з параметраОголошення
glslfloat inversesqrt(float x)
vec2 inversesqrt(vec2 x)
vec3 inversesqrt(vec3 x)
vec4 inversesqrt(vec4 x)
Параметри
`x` — значення, з якого треба взяти обернений квадратний коріньОпис
`inversesqrt()` повертає значення, обернене квадратному кореню з x.<div class="simpleFunction" data="y = inversesqrt(x);"></div>
Дивіться також
pow, sqrt, Розділ 05: Формотворчі функції---
Glossary/Inversesqrt/README Vi (glossary/inversesqrt/README-vi.md)
Inversesqrt
Phép nghịch đảo căn bậc 2.Các phiên bản
glslfloat inversesqrt(float x)
vec2 inversesqrt(vec2 x)
vec3 inversesqrt(vec3 x)
vec4 inversesqrt(vec4 x)
Các tham số
`x` số cần tính nghịch đảo căn bậc 2.Mô tả
`inversesqrt()` trả về kết quả của biểu thức `1 / sqrt(x)`.<div class="simpleFunction" data="y = inversesqrt(x); "></div>
Tham khảo thêm
pow, sqrt, Các hàm số cơ bản (Hàm hình dạng - Shape function)
---
Glossary/Ivec2/README (glossary/ivec2/README.md)
Ivec2
2 dimensional integer vectorDeclaration
glslbvec2 aIvec2 = ivec2(1, 1);
bvec2 bIvec2 = ivec2(1);bvec2 cIvec2 = ivec2(aIvec3);
bvec2 dIvec2 = ivec2(aIvec3.x, aIvec3.y);
Description
`ivec2` is an integer vector with two components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a vector of higher dimension. The respective values are used to initialize the components.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec2/README Ua (glossary/ivec2/README-ua.md)
ivec2
2-вимірний цілочисельний векторОголошення
glslbvec2 aIvec2 = ivec2(1, 1);
bvec2 bIvec2 = ivec2(1);bvec2 cIvec2 = ivec2(aIvec3);
bvec2 dIvec2 = ivec2(aIvec3.x, aIvec3.y);
Опис
`ivec2` — цілочисельний вектор із двома компонентами. Способи ініціалізації:- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання вектора вищої розмірності, де відповідні значення будуть використані для ініціалізації компонентів;
Дивіться також
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec2/README Vi (glossary/ivec2/README-vi.md)
Ivec2
Vector số nguyên 2 chiều.Các phiên bản
glslbvec2 aIvec2 = ivec2(1, 1);
bvec2 bIvec2 = ivec2(1);bvec2 cIvec2 = ivec2(aIvec3);
bvec2 dIvec2 = ivec2(aIvec3.x, aIvec3.y);
Mô tả
`ivec2` vector số nguyên gồm 2 thành phần. Nó có thể được khởi tạo bằng các cách:
- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 2 thành phần
- Lấy giá trị từ 1 vector nhiều chiều hơn. Các thành phần sẽ được khởi tạo theo thứ tự.Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec3/README (glossary/ivec3/README.md)
Ivec3
3 dimensional integer vectorDeclaration
glslvec3 aIvec3 = ivec3(1, 1, 1);
vec3 bIvec3 = ivec3(1);vec3 cIvec3 = ivec3(aIvec4);
vec3 dIvec3 = ivec3(aIvec4.x, aIvec4.y, aIvec4.z);
vec3 eIvec3 = ivec3(aIvec2, aInt);
vec3 fIvec3 = ivec3(aIvec2.x, aIvec2.y, aInt);
Description
`ivec3` is an integer vector with three components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a vector of higher dimension. The respective values are used to initialize the components.
- Providing a combination of vectors and/or scalars. The respective values are used to initialize the vector. The arguments of the constructor must have at least as many components as the vector that is initialized.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec3/README Ua (glossary/ivec3/README-ua.md)
ivec3
3-вимірний цілочисельний векторОголошення
glslvec3 aIvec3 = ivec3(1, 1, 1);
vec3 bIvec3 = ivec3(1);vec3 cIvec3 = ivec3(aIvec4);
vec3 dIvec3 = ivec3(aIvec4.x, aIvec4.y, aIvec4.z);
vec3 eIvec3 = ivec3(aIvec2, aInt);
vec3 fIvec3 = ivec3(aIvec2.x, aIvec2.y, aInt);
Опис
`ivec3` — цілочисельний вектор із трьома компонентами. Способи ініціалізації:- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання вектора вищої розмірності, де відповідні значення будуть використані для ініціалізації компонентів;
- надання комбінації векторів та/або скалярів. Для ініціалізації вектора використовуються відповідні значення. Аргументи конструктора повинні містити принаймні стільки ж компонентів, скільки ініціалізований вектор.
Дивіться також
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec3/README Vi (glossary/ivec3/README-vi.md)
Ivec3
Vector số nguyên 3 chiều.Các phiên bản
glslvec3 aIvec3 = ivec3(1, 1, 1);
vec3 bIvec3 = ivec3(1);vec3 cIvec3 = ivec3(aIvec4);
vec3 dIvec3 = ivec3(aIvec4.x, aIvec4.y, aIvec4.z);
vec3 eIvec3 = ivec3(aIvec2, aInt);
vec3 fIvec3 = ivec3(aIvec2.x, aIvec2.y, aInt);
Mô tả
`ivec3` vector số nguyên gồm 3 thành phần. Nó có thể được khởi tạo bằng các cách:
- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 3 thành phần
- Lấy giá trị từ 1 vector nhiều chiều hơn. Các thành phần sẽ được khởi tạo theo thứ tự.
- Lấy giá trị theo thứ tự lần lượt từ nhiều vector, miễn là đủ 3 thành phần.Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec4/README (glossary/ivec4/README.md)
Ivec4
4 dimensional integer vectorDeclaration
glslvec4 aIvec4 = ivec4(1, 1, 1, 1);
vec4 bIvec4 = ivec4(1);vec4 cIvec4 = ivec4(aIvec2, aInteger, aIvec3);
vec4 dIvec4 = ivec4(aIvec2.x, aIvec2.y, aInt, aIvec3.x);
Description
`ivec4` is an integer vector with four components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a combination of vectors and/or scalars. The respective values are used to initialize the vector. The arguments of the constructor must have at least as many components as the vector that is initialized.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec4/README Ua (glossary/ivec4/README-ua.md)
ivec4
4-вимірний цілочисельний векторОголошення
glslvec4 aIvec4 = ivec4(1, 1, 1, 1);
vec4 bIvec4 = ivec4(1);vec4 cIvec4 = ivec4(aIvec2, aInteger, aIvec3);
vec4 dIvec4 = ivec4(aIvec2.x, aIvec2.y, aInt, aIvec3.x);
Опис
`ivec4` — цілочисельний вектор із чотирма компонентами. Способи ініціалізації:- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання комбінації векторів та/або скалярів. Для ініціалізації вектора використовуються відповідні значення. Аргументи конструктора повинні містити принаймні стільки ж компонентів, скільки ініціалізований вектор.
Дивіться також
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Ivec4/README Vi (glossary/ivec4/README-vi.md)
Ivec4
Vector số nguyên 4 chiềuCác phiên bản
glslvec4 aIvec4 = ivec4(1, 1, 1, 1);
vec4 bIvec4 = ivec4(1);vec4 cIvec4 = ivec4(aIvec2, aInteger, aIvec3);
vec4 dIvec4 = ivec4(aIvec2.x, aIvec2.y, aInt, aIvec3.x);
Mô tả
`ivec4` là một vector số nguyên gồm 4 thành phần. Nó có thể được khởi tạo bằng các cách:- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 4 thành phần
- Lấy giá trị theo thứ tự lần lượt từ nhiều vector, miễn là đủ 4 thành phần.
Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Length/README (glossary/length/README.md)
Length
Calculate the length of a vectorDeclaration
glslfloat length(float x)
float length(vec2 x)
float length(vec3 x)
float length(vec4 x)
Parameters
`x` specifies a vector of which to calculate the length.Description
`length()` returns the length of the vector.<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
See Also
distance(), normalize(), Chapter 07: Shapes
---
Glossary/Length/README Ua (glossary/length/README-ua.md)
length
Обчислює довжину вектораОголошення
glslfloat length(float x)
float length(vec2 x)
float length(vec3 x)
float length(vec4 x)
Параметри
`x` — вектор, довжину якого потрібно обчислити.Опис
`length()` повертає довжину вектора.<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
Дивіться також
distance(), normalize(), Розділ 07: Фігури---
Glossary/Length/README Vi (glossary/length/README-vi.md)
Length
Tính độ dài cuuar vector.Các phiên bản
glslfloat length(float x)
float length(vec2 x)
float length(vec3 x)
float length(vec4 x)
Các tham số
`x` vector cần tính độ dàiMô tả
`length()` trả về độ dài của vector.<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
Tham khảo thêm
distance(), normalize(), Chương 07: Hình dạng
---
Glossary/LessThan/README (glossary/lessThan/README.md)
LessThan
Perform a component-wise less-than comparison of two vectorsDeclaration
glslbvec2 lessThan(vec2 x, vec2 y)
bvec3 lessThan(vec3 x, vec3 y)
bvec4 lessThan(vec4 x, vec4 y) bvec2 lessThan(ivec2 x, ivec2 y)
bvec3 lessThan(ivec3 x, ivec3 y)
bvec4 lessThan(ivec4 x, ivec4 y)
Parameters
`x` specifies the first vector to be used in the comparison operation.`y` specifies the second vector to be used in the comparison operation.Description
`lessThan()` returns a boolean vector in which each element `i` is computed as `x[i] < y[i]`.See Also
lessThanEqual(), greaterThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/LessThan/README Ua (glossary/lessThan/README-ua.md)
lessThan
Виконує по-компонентне порівняння двох векторів на меншість компонентів першого вектора по відношенню до другогоОголошення
glslbvec2 lessThan(vec2 x, vec2 y)
bvec3 lessThan(vec3 x, vec3 y)
bvec4 lessThan(vec4 x, vec4 y) bvec2 lessThan(ivec2 x, ivec2 y)
bvec3 lessThan(ivec3 x, ivec3 y)
bvec4 lessThan(ivec4 x, ivec4 y)
Параметри
`x` — перший вектор для порівняння.
`y` — другий вектор для порівняння.Опис
`lessThan()` повертає булів вектор, у якому кожен елемент i обчислюється як "`x[i] < y[i]`".Дивіться також
lessThanEqual(), greaterThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/LessThan/README Vi (glossary/lessThan/README-vi.md)
LessThan
Thực hiện phép so sánh nhỏ-hơn giữa các cặp giá trị của 2 vector.Các phiên bản
glslbvec2 lessThan(vec2 x, vec2 y)
bvec3 lessThan(vec3 x, vec3 y)
bvec4 lessThan(vec4 x, vec4 y) bvec2 lessThan(ivec2 x, ivec2 y)
bvec3 lessThan(ivec3 x, ivec3 y)
bvec4 lessThan(ivec4 x, ivec4 y)
Các tham số
`x` Vector thứ nhất.`y` Vector thứ hai.Mô tả
`lessThan()` trả về một vector boolean mà thành phần thứ `i` là kết quả của phép so sánh `x[i] < y[i]`.Tham khảo thêm
lessThanEqual(), greaterThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/LessThanEqual/README (glossary/lessThanEqual/README.md)
LessThanEqual
Perform a component-wise less-than-or-equal comparison of two vectorsDeclaration
glslbvec2 lessThanEqual(vec2 x, vec2 y)
bvec3 lessThanEqual(vec3 x, vec3 y)
bvec4 lessThanEqual(vec4 x, vec4 y) bvec2 lessThanEqual(ivec2 x, ivec2 y)
bvec3 lessThanEqual(ivec3 x, ivec3 y)
bvec4 lessThanEqual(ivec4 x, ivec4 y)
Parameters
`x` specifies the first vector to be used in the comparison operation.`y` specifies the second vector to be used in the comparison operation.Description
`lessThanEqual()` returns a boolean vector in which each element `i` is computed as `x[i] ≤ y[i]`.See Also
lessThan(), greaterThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/LessThanEqual/README Ua (glossary/lessThanEqual/README-ua.md)
lessThanEqual
Виконує по-компонентне порівняння двох векторів на меншість або рівність компонентів першого вектора по відношенню до другогоОголошення
glslbvec2 lessThanEqual(vec2 x, vec2 y)
bvec3 lessThanEqual(vec3 x, vec3 y)
bvec4 lessThanEqual(vec4 x, vec4 y) bvec2 lessThanEqual(ivec2 x, ivec2 y)
bvec3 lessThanEqual(ivec3 x, ivec3 y)
bvec4 lessThanEqual(ivec4 x, ivec4 y)
Параметри
`x` — перший вектор для порівняння.
`y` — другий вектор для порівняння.Опис
`lessThanEqual()` повертає булів вектор, у якому кожен елемент i обчислюється як "`x[i] ≤ y[i]`".Дивіться також
lessThan(), greaterThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/LessThanEqual/README Vi (glossary/lessThanEqual/README-vi.md)
LessThanEqual
Thực hiện phép so sánh nhỏ-hơn-hoặc-bằng giữa các cặp giá trị của 2 vector.Các phiên bản
glslbvec2 lessThanEqual(vec2 x, vec2 y)
bvec3 lessThanEqual(vec3 x, vec3 y)
bvec4 lessThanEqual(vec4 x, vec4 y) bvec2 lessThanEqual(ivec2 x, ivec2 y)
bvec3 lessThanEqual(ivec3 x, ivec3 y)
bvec4 lessThanEqual(ivec4 x, ivec4 y)
Các tham số
`x` Vector thứ nhất.`y` Vector thứ hai.Mô tả
`lessThanEqual()` trả về một vector boolean mà thành phần thứ `i` là kết quả của phép so sánh `x[i] ≤ y[i]`.Tham khảo thêm
lessThan(), greaterThan(), greaterThanEqual(), equal(), notEqual(), any(), all(), not()---
Glossary/Log/README (glossary/log/README.md)
Log
Return the natural logarithm of the parameterDeclaration
glslfloat log(float x)
vec2 log(vec2 x)
vec3 log(vec3 x)
vec4 log(vec4 x)
Parameters
`x` specifies the value of which to take the natural logarithm.Description
`log()` returns the natural logarithm of `x`.<div class="simpleFunction" data="y = log(x); "></div>
See Also
log2, exp, exp2, Chapter 05: Shaping Functions
---
Glossary/Log/README Ua (glossary/log/README-ua.md)
log
Повертає натуральний логарифм параметра.
Тобто повертає значення до якого степеня слід піднести число Ейлера, щоб одержати вказане число. Число Ейлера або e приблизно дорівнює 2.718281828.Оголошення
glslfloat log(float x)
vec2 log(vec2 x)
vec3 log(vec3 x)
vec4 log(vec4 x)
Параметри
`x` — значення, яке потрібно взяти в натуральний логарифм.Опис
`log()` повертає натуральний логарифм числа x.<div class="simpleFunction" data="y = log(x); "></div>
Дивіться також
log2, exp, exp2, Розділ 05: Формотворчі функції---
Glossary/Log/README Vi (glossary/log/README-vi.md)
Log
Tính logarith tự nhiên của một sốCác phiên bản
glslfloat log(float x)
vec2 log(vec2 x)
vec3 log(vec3 x)
vec4 log(vec4 x)
Các tham số
`x` số cần tính logarithMô tả
`log()` trả về kết quả của phép tính `log(x)`.<div class="simpleFunction" data="y = log(x); "></div>
Tham khảo thêm
log2, exp, exp2, Các hàm số cơ bản (Hàm hình dạng - Shape function)
---
Glossary/Log2/README (glossary/log2/README.md)
Log2
Return the base 2 logarithm of the parameterDeclaration
glslfloat log2(float x)
vec2 log2(vec2 x)
vec3 log2(vec3 x)
vec4 log2(vec4 x)
Parameters
`x` specifies the value of which to take the base 2 logarithm.Description
`log2()` returns the base 2 logarithm of `x`.<div class="simpleFunction" data="y = log2(x); "></div>
See Also
log, exp, exp2, Chapter 05: Shaping Functions
---
Glossary/Log2/README Ua (glossary/log2/README-ua.md)
log2
Повертає логарифм числа за основою 2.
Тобто повертає степінь яка необхідна для двійки, щоб отримати передане число.Оголошення
glslfloat log2(float x)
vec2 log2(vec2 x)
vec3 log2(vec3 x)
vec4 log2(vec4 x)
Параметри
`x` — значення, яке потрібно взяти за логарифмом з основою 2.Опис
`log2()` повертає логарифм числа x з основою 2.<div class="simpleFunction" data="y = log2(x); "></div>
Дивіться також
log, exp, exp2, Розділ 05: Формотворчі функції---
Glossary/Log2/README Vi (glossary/log2/README-vi.md)
Log2
Tính logarith cơ số 2.Các phiên bản
glslfloat log2(float x)
vec2 log2(vec2 x)
vec3 log2(vec3 x)
vec4 log2(vec4 x)
Các tham số
`x` giá trị cần tính logarith cơ số 2.Mô tả
`log2()` trả về kết quả của hàm số logarith cơ số 2 của `x`.<div class="simpleFunction" data="y = log2(x); "></div>
Tham khảo thêm
log, exp, exp2, Các hàm số cơ bản (Hàm hình dạng - Shape function)
---
Glossary/Lowp/README (glossary/lowp/README.md)
Lowp
Declaration / Example
Parameters
Description
See Also
---
Glossary/Lowp/README Ua (glossary/lowp/README-ua.md)
lowp
Кваліфікатор точності, задається на початку шейдераПриклад
glslprecision lowp float;
precision lowp int;
Опис
Кваліфікатор точності оголошує мінімальний діапазон і точність, які повинні використовуватися під час зберігання змінних відповідного типу.
`lowp` — 8+ біт, діапазон для float від -2 до 2, діапазон для int від -2^8 до 2^8Дивіться також
mediump, highp, precision---
Glossary/Lowp/README Vi (glossary/lowp/README-vi.md)
Lowp
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Main/README (glossary/main/README.md)
Main
Declaration / Example
Parameters
Description
See Also
---
Glossary/Main/README Ua (glossary/main/README-ua.md)
main
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Main/README Vi (glossary/main/README-vi.md)
Main
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Mat2/README (glossary/mat2/README.md)
Mat2
2x2 floating point matrixDeclaration
glslmat2 aMat2 = mat2(1.0, 0.0, // 1. column
0.0, 1.0); // 2. column
mat2 bMat2 = mat2(1.0);
mat2 cMat2 = mat2(aVec2, bVec2);
mat2 dMat2 = mat2(aVec3, aFloat);
Description
`mat2` data type is compose for a 2x2 matrix of floating point. As you can see above, can be initialize in different ways:- Providing a value for each component column by column.
- Providing one value that is used for the components on the main diagonal.
- Providing a combination of vectors and scalars.
In the same way data can be accessed component-wise or column by column:
glslmat2 aMat2;
aMat2[1][1] = 1.0;
float aFloat = aMat2[1][1];aMat2[0] = vec2(1.0);
vec2 aVec2 = aMat2[0];
See Also
mat3, mat4, matrixCompMult()---
Glossary/Mat2/README Ua (glossary/mat2/README-ua.md)
mat2
Матриця розміром 2x2 зі значеннями типу floatОголошення
glslmat2 aMat2 = mat2(
1.0, 0.0, // 1. column
0.0, 1.0 // 2. column
);
mat2 bMat2 = mat2(1.0);mat2 cMat2 = mat2(aVec2, bVec2);
mat2 dMat2 = mat2(aVec3, aFloat);
Опис
`mat2` — тип даних, що є матрицею розміром 2x2 із значеннями типу float. Як видно із прикладу вище, ініціалізувати можна різними способами:- надання значень для кожного компонента стовпець за стовпцем;
- надання одного значення, яке використовується для компонентів на головній діагоналі;
- надання комбінації векторів і скалярів;
Так само можна отримати доступ до даних покомпонентно або стовпець за стовпцем:
glslmat2 aMat2;
aMat2[1][1] = 1.0;
float aFloat = aMat2[1][1];aMat2[0] = vec2(1.0);
vec2 aVec2 = aMat2[0];
Дивіться також
mat3, mat4, matrixCompMult()---
Glossary/Mat2/README Vi (glossary/mat2/README-vi.md)
Mat2
Ma trận số thực 2x2Các phiên bản
glslmat2 aMat2 = mat2(1.0, 0.0, // Cột thứ nhất
0.0, 1.0); // Cột thứ hai
mat2 bMat2 = mat2(1.0);
mat2 cMat2 = mat2(aVec2, bVec2);
mat2 dMat2 = mat2(aVec3, aFloat);
Mô tả
`mat2` là ma trận số thực gồm 2 hàng và 2 cột. Nó có thể được khởi tạo bằng các cách:- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho các thành phần trên đường chéo chính
- Chỉ định giá trị từng cột, mỗi cột coi như một
`vec2textCó thể lấy giá trị của từng thành phần trong ma trận hoặc lấy từng cột:
glsl
mat2 aMat2;
aMat2[1][1] = 1.0;
float aFloat = aMat2[1][1];aMat2[0] = vec2(1.0);
vec2 aVec2 = aMat2[0];
textTham khảo thêm
mat3, mat4, matrixCompMult()---
Glossary/Mat3/README (glossary/mat3/README.md)
Mat3
3x3 floating point matrixDeclaration
glsl
mat3 aMat3 = mat3(1.0, 0.0, 0.0, // 1. column
0.0, 1.0, 0.0, // 2. column
0.0, 0.0, 1.0); // 3. column
mat3 bMat3 = mat3(1.0);mat3 cMat3 = mat3(aVec3, bVec3, cVec3);
mat3 dMat3 = mat3(aVec4, aVec3, bVec4, aFloat);
textDescription
mat3` data type is compose for a 3x3 matrix of floating point. As you can see above, can be initialize in different ways:- Providing a value for each component column by column.
- Providing one value that is used for the components on the main diagonal.
- Providing a combination of vectors and scalars.
In the same way data can be accessed component-wise or column by column:
glslmat3 aMat3;
aMat3[2][2] = 1.0;
float aFloat = aMat3[2][2];aMat3[0] = vec3(1.0);
vec3 aVec3 = aMat3[0];
See Also
mat2, mat4, matrixCompMult()---
Glossary/Mat3/README Ua (glossary/mat3/README-ua.md)
mat3
Матриця розміром 3x3 зі значеннями типу floatОголошення
glslmat3 aMat3 = mat3(
1.0, 0.0, 0.0, // 1. column
0.0, 1.0, 0.0, // 2. column
0.0, 0.0, 1.0 // 3. column
);
mat3 bMat3 = mat3(1.0);mat3 cMat3 = mat3(aVec3, bVec3, cVec3);
mat3 dMat3 = mat3(aVec4, aVec3, bVec4, aFloat);
Опис
`mat3` — тип даних, що є матрицею розміром 3x3 із значеннями типу float. Як видно із прикладу вище, ініціалізувати можна різними способами:- надання значень для кожного компонента стовпець за стовпцем;
- надання одного значення, яке використовується для компонентів на головній діагоналі;
- надання комбінації векторів і скалярів;
Так само можна отримати доступ до даних покомпонентно або стовпець за стовпцем:
glslmat3 aMat3;
aMat3[2][2] = 1.0;
float aFloat = aMat3[2][2];aMat3[0] = vec3(1.0);
vec3 aVec3 = aMat3[0];
Дивіться також
mat2, mat4, matrixCompMult()---
Glossary/Mat3/README Vi (glossary/mat3/README-vi.md)
Mat3
Ma trận số thực 3x3Các phiên bản
glslmat3 aMat3 = mat3(1.0, 0.0, 0.0, // Cột thứ 1
0.0, 1.0, 0.0, // Cột thứ 2
0.0, 0.0, 1.0); // Cột thứ 3
mat3 bMat3 = mat3(1.0);mat3 cMat3 = mat3(aVec3, bVec3, cVec3);
mat3 dMat3 = mat3(aVec4, aVec3, bVec4, aFloat);
Mô tả
`mat3` là ma trận số thực gồm 3 hàng và 3 cột. Nó có thể được khởi tạo bằng các cách:- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho các thành phần trên đường chéo chính
- Chỉ định giá trị từng cột, mỗi cột coi như một
`vec3textCó thể lấy giá trị của từng thành phần trong ma trận hoặc lấy từng cột:
glsl
mat3 aMat3;
aMat3[2][2] = 1.0;
float aFloat = aMat3[2][2];aMat3[0] = vec3(1.0);
vec3 aVec3 = aMat3[0];
textTham khảo thêm
mat2, mat4, matrixCompMult()---
Glossary/Mat4/README (glossary/mat4/README.md)
Mat4
4x4 floating point matrixDeclaration
glsl
mat4 aMat4 = mat4(1.0, 0.0, 0.0, 0.0, // 1. column
0.0, 1.0, 0.0, 0.0, // 2. column
0.0, 0.0, 1.0, 0.0, // 3. column
0.0, 0.0, 0.0, 1.0); // 4. column
mat4 bMat4 = mat4(1.0);mat4 cMat4 = mat4(aVec4, bVec4, cVec4, dVec4);
mat4 dMat4 = mat4(aVec4, aVec3, bVec4, cVec4, aFloat);
textDescription
mat4` data type is compose for a 4x4 matrix of floating point. As you can see above, can be initialize in different ways:- Providing a value for each component column by column.
- Providing one value that is used for the components on the main diagonal.
- Providing a combination of vectors and scalars.
In the same way data can be accessed component-wise or column by column:
glslaMat4[3][3] = 1.0;
float aFloat = aMat4[3][3];aMat4[0] = vec4(1.0);
vec4 aVec4 = aMat4[0];
See Also
mat2, mat3, matrixCompMult()---
Glossary/Mat4/README Vi (glossary/mat4/README-vi.md)
Mat4
Ma trận số thực 4x4Các phiên bản
glslmat4 aMat4 = mat4(1.0, 0.0, 0.0, 0.0, // Cột thứ 1
0.0, 1.0, 0.0, 0.0, // Cột thứ 2
0.0, 0.0, 1.0, 0.0, // Cột thứ 3
0.0, 0.0, 0.0, 1.0); // Cột thứ 4
mat4 bMat4 = mat4(1.0);mat4 cMat4 = mat4(aVec4, bVec4, cVec4, dVec4);
mat4 dMat4 = mat4(aVec4, aVec3, bVec4, cVec4, aFloat);
Mô tả
`mat4` là ma trận số thực gồm 4 hàng và 4 cột. Nó có thể được khởi tạo bằng các cách:- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho các thành phần trên đường chéo chính
- Chỉ định giá trị từng cột, mỗi cột coi như một
`vec4textCó thể lấy giá trị của từng thành phần trong ma trận hoặc lấy từng cột:
glsl
aMat4[3][3] = 1.0;
float aFloat = aMat4[3][3];aMat4[0] = vec4(1.0);
vec4 aVec4 = aMat4[0];
textTham khảo thêm
mat2, mat3, matrixCompMult()---
Glossary/MatrixCompMult/README (glossary/matrixCompMult/README.md)
MatrixCompMult
Perform a component-wise multiplication of two matricesDeclaration
glsl
mat2 matrixCompMult(mat2 x, mat2 y)
mat3 matrixCompMult(mat3 x, mat3 y)
mat4 matrixCompMult(mat4 x, mat4 y)
textParameters
x` specifies the first matrix multiplicand.`y` specifies the second matrix multiplicand.Description
`matrixCompMult()` performs a component-wise multiplication of two matrices, yielding a result matrix where each component, `result[i][j]` is computed as the scalar product of `x[i][j]` and `y[i][j]`.See Also
Chapter 08: Matrix---
Glossary/MatrixCompMult/README Ua (glossary/matrixCompMult/README-ua.md)
matrixCompMult
Виконує покомпонентне множення двох матрицьОголошення
glslmat2 matrixCompMult(mat2 x, mat2 y)
mat3 matrixCompMult(mat3 x, mat3 y)
mat4 matrixCompMult(mat4 x, mat4 y)
Параметри
`x` — перший матричний множник.
`y` — другий матричний множник.Опис
`matrixCompMult()` виконує покомпонентне множення двох матриць, повертаючи нову матрицю, де кожен компонент `result[i][j]` обчислюється як скалярний добуток `x[i][j]` та `y[i][j]`.Дивіться також
mat2, mat3, mat4, Розділ 08: Матриці---
Glossary/MatrixCompMult/README Vi (glossary/matrixCompMult/README-vi.md)
MatrixCompMult
Nhân từng cặp giá trị tương ứng giữa 2 ma trận.Các phiên bản
glslmat2 matrixCompMult(mat2 x, mat2 y)
mat3 matrixCompMult(mat3 x, mat3 y)
mat4 matrixCompMult(mat4 x, mat4 y)
Các tham số
`x` Ma trận thứ nhất.`y` Ma trận thứ hai.Mô tả
`matrixCompMult()` trả về ma trận kết quả, với từng thành phần `result[i][j]` là kết quả của phép tính `x[i][j] * y[i][j]`.Tham khảo thêm
Chương 08: Ma trận---
Glossary/Max/README (glossary/max/README.md)
Max
Return the greater of two valuesDeclaration
glslfloat max(float x, float y)
vec2 max(vec2 x, vec2 y)
vec3 max(vec3 x, vec3 y)
vec4 max(vec4 x, vec4 y)vec2 max(vec2 x, float y)
vec3 max(vec3 x, float y)
vec4 max(vec4 x, float y)
Parameters
`x` specifies the first value to compare.`y` specifies the second value to compare.Description
`max()` returns the maximum of the two parameters. It returns `y` if `y` is greater than `x`, otherwise it returns `x`.<div class="simpleFunction" data="y = max(x,0.5); "></div>
See Also
min, abs, clamp, Chapter 05: Shaping Functions---
Glossary/Max/README Ua (glossary/max/README-ua.md)
max
Повертає більше з двох значеньОголошення
glslfloat max(float x, float y)
vec2 max(vec2 x, vec2 y)
vec3 max(vec3 x, vec3 y)
vec4 max(vec4 x, vec4 y)vec2 max(vec2 x, float y)
vec3 max(vec3 x, float y)
vec4 max(vec4 x, float y)
Параметри
`x` — перше значення для порівняння.
`y` — друге значення для порівняння.Опис
`max()` повертає максимальне значення із двох параметрів. Повертає y, якщо він більше за x, інакше повертає x.<div class="simpleFunction" data="y = max(x,0.5);"></div>
Дивіться також
min, abs, clamp, Розділ 05: Формотворчі функції---
Glossary/Max/README Vi (glossary/max/README-vi.md)
Max
Tìm giá trị lớn hơn giữa 2 giá trị.Các phiên bản
glslfloat max(float x, float y)
vec2 max(vec2 x, vec2 y)
vec3 max(vec3 x, vec3 y)
vec4 max(vec4 x, vec4 y)vec2 max(vec2 x, float y)
vec3 max(vec3 x, float y)
vec4 max(vec4 x, float y)
Các tham số
`x` Giá trị thứ nhất.`y` Giá trị thứ hai.Mô tả
`max()` thực hiện phép so sánh giữa từng cặp giá trị trong hai vector `x` và `y` để chọn ra các số lớn hơn ở mỗi cặp giá trị.<div class="simpleFunction" data="y = max(x,0.5); "></div>
Tham khảo thêm
min, abs, clamp, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Mediump/README (glossary/mediump/README.md)
Mediump
Declaration / Example
Parameters
Description
See Also
---
Glossary/Mediump/README Ua (glossary/mediump/README-ua.md)
mediump
Кваліфікатор точності, задається на початку шейдераПриклад
glslprecision mediump float;
precision mediump int;
Опис
Кваліфікатор точності оголошує мінімальний діапазон і точність, які повинні використовуватися під час зберігання змінних відповідного типу.
`mediump` — 16+ біт, діапазон для float від -2^14 to +2^14, діапазон для int від -2^10 до 2^10Дивіться також
lowp, highp, precision---
Glossary/Mediump/README Vi (glossary/mediump/README-vi.md)
Mediump
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Min/README (glossary/min/README.md)
Min
Return the lesser of two valuesDeclaration
glslfloat min(float x, float y)
vec2 min(vec2 x, vec2 y)
vec3 min(vec3 x, vec3 y)
vec4 min(vec4 x, vec4 y)vec2 min(vec2 x, float y)
vec3 min(vec3 x, float y)
vec4 min(vec4 x, float y)
Parameters
`x` specifies the first value to compare.`y` specifies the second value to compare.Description
`min()` returns the minimum of the two parameters. It returns `y` if `y` is less than `x`, otherwise it returns `x`.<div class="simpleFunction" data="y = min(x,0.5); "></div>
See Also
max, abs, clamp, Chapter 05: Shaping Functions---
Glossary/Min/README Ua (glossary/min/README-ua.md)
min
Повертає менше з двох значеньОголошення
glslfloat min(float x, float y)
vec2 min(vec2 x, vec2 y)
vec3 min(vec3 x, vec3 y)
vec4 min(vec4 x, vec4 y)vec2 min(vec2 x, float y)
vec3 min(vec3 x, float y)
vec4 min(vec4 x, float y)
Параметри
`x` — перше значення для порівняння.
`y` — друге значення для порівняння.Опис
`min()` повертає мінімальне значення із двох параметрів. Повертає y, якщо він менше за x, інакше повертає x.<div class="simpleFunction" data="y = min(x,0.5);"></div>
Дивіться також
max, abs, clamp, Розділ 05: Формотворчі функції---
Glossary/Min/README Vi (glossary/min/README-vi.md)
Min
Tìm giá trị nhỏ hơn giữa 2 giá trị.Các phiên bản
glslfloat min(float x, float y)
vec2 min(vec2 x, vec2 y)
vec3 min(vec3 x, vec3 y)
vec4 min(vec4 x, vec4 y)vec2 min(vec2 x, float y)
vec3 min(vec3 x, float y)
vec4 min(vec4 x, float y)
Các tham số
`x` Giá trị thứ nhất.`y` Giá trị thứ hai.Mô tả
`min()` thực hiện phép so sánh giữa từng cặp giá trị trong hai vector `x` và `y` để chọn ra các số nhỏ hơn ở mỗi cặp giá trị.<div class="simpleFunction" data="y = min(x,0.5); "></div>
Tham khảo thêm
max, abs, clamp, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Mix/README (glossary/mix/README.md)
Mix
Constrain a value to lie between two further valuesDeclaration
glslfloat mix(float x, float y, float a)
vec2 mix(vec2 x, vec2 y, vec2 a)
vec3 mix(vec3 x, vec3 y, vec3 a)
vec4 mix(vec4 x, vec4 y, vec4 a)vec2 mix(vec2 x, vec2 y, float a)
vec3 mix(vec3 x, vec3 y, float a)
vec4 mix(vec4 x, vec4 y, float a)
Parameters
`x` specifies the start of the range in which to interpolate.`y` specifies the end of the range in which to interpolate.`a` specifies the value to use to interpolate between x and y.Description
`mix()` performs a linear interpolation between `x` and `y` using `a` to weight between them. The return value is computed as `x×(1−a)+y×a`.<div class="codeAndCanvas" data="../06/mix.frag"></div>
<div class="codeAndCanvas" data="../06/gradient.frag"></div>
See Also
---
Glossary/Mix/README Ua (glossary/mix/README-ua.md)
mix
Виконує лінійну інтерполяцію між двома значеннями.Оголошення
glslfloat mix(float x, float y, float a)
vec2 mix(vec2 x, vec2 y, vec2 a)
vec3 mix(vec3 x, vec3 y, vec3 a)
vec4 mix(vec4 x, vec4 y, vec4 a)vec2 mix(vec2 x, vec2 y, float a)
vec3 mix(vec3 x, vec3 y, float a)
vec4 mix(vec4 x, vec4 y, float a)
Параметри
`x` — початок діапазону інтерполяції.
`y` — кінець діапазону інтерполяції.
`a` — процентне значення ваги для інтерполяції, від 0.0 до 1.0.Опис
`mix()` виконує лінійну інтерполяцію між x та y використовуючи a для зважування між ними. Повернене значення обчислюється як "`x (1 − a) + y a`".<div class="codeAndCanvas" data="../06/mix.frag"></div>
<div class="codeAndCanvas" data="../06/gradient.frag"></div>
Дивіться також
min, max, Розділ 06: Кольори---
Glossary/Mix/README Vi (glossary/mix/README-vi.md)
Mix
Nội suy tuyến tính giữa 2 giá trịCác phiên bản
glslfloat mix(float x, float y, float a)
vec2 mix(vec2 x, vec2 y, vec2 a)
vec3 mix(vec3 x, vec3 y, vec3 a)
vec4 mix(vec4 x, vec4 y, vec4 a)vec2 mix(vec2 x, vec2 y, float a)
vec3 mix(vec3 x, vec3 y, float a)
vec4 mix(vec4 x, vec4 y, float a)
Các tham số
`x` Giá trị nhỏ hơn trong khoảng cần nội suy.`y` Giá trị lớn hơn trong khoảng cần nội suy.`a` Tỉ lệ nội suy. Mô tả
`mix()` nội suy giá trị giữa `x` và `y` sử dụng `a` làm tỉ lệ nội suy / trọng số. Kết quả của phép tính `x×(1−a)+y×a`.<div class="codeAndCanvas" data="../06/mix.frag"></div>
<div class="codeAndCanvas" data="../06/gradient.frag"></div>
Tham khảo thêm
---
Glossary/Mod/README (glossary/mod/README.md)
Mod
Compute value of one parameter modulo anotherDeclaration
glslfloat mod(float x, float y)
vec2 mod(vec2 x, vec2 y)
vec3 mod(vec3 x, vec3 y)
vec4 mod(vec4 x, vec4 y)vec2 mod(vec2 x, float y)
vec3 mod(vec3 x, float y)
vec4 mod(vec4 x, float y)
Parameters
`x` specifies the value to evaluate.
`y` specifies the value to obtain the modulo of.Description
`mod()` returns the value of `x` modulo `y`. This is computed as `x - y * floor(x/y)`.<div class="simpleFunction" data="y = mod(x,1.5); "></div>
See Also
floor, fract, ceil, Chapter 05: Shaping Functions---
Glossary/Mod/README Ua (glossary/mod/README-ua.md)
mod
Повертає залишок від ділення першого параметра на другий. Результат має такий самий знак, як і дільник.Оголошення
glslfloat mod(float x, float y)
vec2 mod(vec2 x, vec2 y)
vec3 mod(vec3 x, vec3 y)
vec4 mod(vec4 x, vec4 y)vec2 mod(vec2 x, float y)
vec3 mod(vec3 x, float y)
vec4 mod(vec4 x, float y)
Параметри
`x` — число, для якого потрібно знайти залишок.
`y` — число, на яке потрібно виконати ділення.Опис
`mod()` повертає значення операції модуля x на y. Обчислюється як "`x - y ⋅ floor(x / y)`".<div class="simpleFunction" data="y = mod(x,1.5);"></div>
Дивіться також
floor, fract, ceil, Розділ 05: Формотворчі функції---
Glossary/Mod/README Vi (glossary/mod/README-vi.md)
Mod
Phép tính phần dưCác phiên bản
glslfloat mod(float x, float y)
vec2 mod(vec2 x, vec2 y)
vec3 mod(vec3 x, vec3 y)
vec4 mod(vec4 x, vec4 y)vec2 mod(vec2 x, float y)
vec3 mod(vec3 x, float y)
vec4 mod(vec4 x, float y)
Các tham số
`x` số bị chia.
`y` số chia.Mô tả
`mod()` trả về kết quả là phần dư của phép chia `x` cho `y`, theo công thức `x - y * floor(x/y)`.<div class="simpleFunction" data="y = mod(x,1.5); "></div>
Tham khảo thêm
floor, fract, ceil, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Normalize/README (glossary/normalize/README.md)
Normalize
Calculate the unit vector in the same direction as the input vectorDeclaration
glslfloat normalize(float x)
vec2 normalize(vec2 x)
vec3 normalize(vec3 x)
vec4 normalize(vec4 x)
Parameters
`x` specifies the vector to normalize.Description
`normalize()` returns a vector with the same direction as its parameter, `x`, but with length 1.See Also
---
Glossary/Normalize/README Ua (glossary/normalize/README-ua.md)
normalize
Обчислює одиничний вектор у тому ж напрямку, що й вхідний векторОголошення
glslfloat normalize(float x)
vec2 normalize(vec2 x)
vec3 normalize(vec3 x)
vec4 normalize(vec4 x)
Параметри
`x` — вектор який потрібно нормалізувати.Опис
`normalize()` повертає вектор із тим же напрямком, що і його параметр x, але з довжиною 1.Дивіться також
length()---
Glossary/Normalize/README Vi (glossary/normalize/README-vi.md)
Normalize
Chuẩn hóa vector.Các phiên bản
glslfloat normalize(float x)
vec2 normalize(vec2 x)
vec3 normalize(vec3 x)
vec4 normalize(vec4 x)
Các tham số
`x` Vector cần chuẩn hóa.Mô tả
`normalize()` trả về một vector cùng chiều với `x` và có độ dài bằng 1.Tham khảo thêm
---
Glossary/Not/README (glossary/not/README.md)
Not
Logically invert a boolean vectorDeclaration
glslbvec2 not(bvec2 x)
bvec3 not(bvec3 x)
bvec4 not(bvec4 x)
Parameters
`x` specifies the vector to be inverted.Description
`not()` logically inverts the boolean vector `x`. It returns a new boolean vector for which each element `i` is computed as `!x[i]`.See Also
any(), all()---
Glossary/Not/README Ua (glossary/not/README-ua.md)
not
Логічно інвертує булевий векторОголошення
glslbvec2 not(bvec2 x)
bvec3 not(bvec3 x)
bvec4 not(bvec4 x)
Параметри
`x` — вектор, який потрібно інвертувати.Опис
`not()` логічно інвертує булів вектор x. Повертає новий булів вектор, для якого кожен елемент i обчислено як !x[i].Дивіться також
any(), all()---
Glossary/Not/README Vi (glossary/not/README-vi.md)
Not
Phép logic nghịch đảo.Các phiên bản
glslbvec2 not(bvec2 x)
bvec3 not(bvec3 x)
bvec4 not(bvec4 x)
Các tham số
`x` giá trị boolean cần đảo ngược.Mô tả
`not()` đảo ngược `x`. Kết quả là một vector boolean mà thành phần thứ `i` là kết quả của phép logic `!x[i]`.Tham khảo thêm
any(), all()---
Glossary/NotEqual/README (glossary/notEqual/README.md)
NotEqual
Perform a component-wise not-equal-to comparison of two vectorsDeclaration
glslbvec2 notEqual(vec2 x, vec2 y)
bvec3 notEqual(vec3 x, vec3 y)
bvec4 notEqual(vec4 x, vec4 y) bvec2 notEqual(ivec2 x, ivec2 y)
bvec3 notEqual(ivec3 x, ivec3 y)
bvec4 notEqual(ivec4 x, ivec4 y)
Parameters
`x` specifies the first vector to be used in the comparison operation.`y` specifies the second vector to be used in the comparison operation.Description
`notEqual()` returns a boolean vector in which each element `i` is computed as `x[i] != y[i]`.See Also
lessThanEqual(), lessThan(), greaterThanEqual(), greaterThan(), equal(), any(), all(), not()---
Glossary/NotEqual/README Ua (glossary/notEqual/README-ua.md)
notEqual
Виконує по-компонентне порівняння двох векторів на нерівністьОголошення
glslbvec2 notEqual(vec2 x, vec2 y)
bvec3 notEqual(vec3 x, vec3 y)
bvec4 notEqual(vec4 x, vec4 y) bvec2 notEqual(ivec2 x, ivec2 y)
bvec3 notEqual(ivec3 x, ivec3 y)
bvec4 notEqual(ivec4 x, ivec4 y)
Параметри
`x` — перший вектор для порівняння.
`y` — другий вектор для порівняння.Опис
`notEqual()` повертає булів вектор, у якому кожен елемент i обчислюється як "`x[i] != y[i]`".Дивіться також
lessThanEqual(), lessThan(), greaterThanEqual(), greaterThan(), equal(), any(), all(), not()---
Glossary/NotEqual/README Vi (glossary/notEqual/README-vi.md)
NotEqual
Thực hiện phép so sánh không-bằng giữa các cặp giá trị của 2 vector.Các phiên bản
glslbvec2 notEqual(vec2 x, vec2 y)
bvec3 notEqual(vec3 x, vec3 y)
bvec4 notEqual(vec4 x, vec4 y) bvec2 notEqual(ivec2 x, ivec2 y)
bvec3 notEqual(ivec3 x, ivec3 y)
bvec4 notEqual(ivec4 x, ivec4 y)
Các tham số
`x` Vector thứ nhất.`y` Vector thứ hai.Mô tả
`notEqual()` trả về một vector boolean mà thành phần thứ `i` là kết quả của phép so sánh `x[i] != y[i]`.Tham khảo thêm
lessThanEqual(), lessThan(), greaterThanEqual(), greaterThan(), equal(), any(), all(), not()---
Glossary/Pow/README (glossary/pow/README.md)
Pow
Return the value of the first parameter raised to the power of the second.Declaration
glslfloat pow(float x, float y)
vec2 pow(vec2 x, vec2 y)
vec3 pow(vec3 x, vec3 y)
vec4 pow(vec4 x, vec4 y)
Parameters
`x` specifies the value to raise to the power `y`.`y` specifies the power to which to raise `x`.Description
`pow()` returns the value of `x` raised to the `y` power.<div class="simpleFunction" data="y = pow(x,3.0); "></div>
See Also
inversesqrt, sqrt, Chapter 05: Shaping Functions
---
Glossary/Pow/README Ua (glossary/pow/README-ua.md)
pow
Підносить значення першого параметра в заданому степені другим параметром.Оголошення
glslfloat pow(float x, float y)
vec2 pow(vec2 x, vec2 y)
vec3 pow(vec3 x, vec3 y)
vec4 pow(vec4 x, vec4 y)
Параметри
`x` — значення, яке потрібно піднести до степеня y.
`y` — степінь, до якої потрібно піднести x.Опис
`pow()` повертає значення x, зведене до степеня y.<div class="simpleFunction" data="y = pow(x, 3.0);"></div>
Дивіться також
inversesqrt, sqrt, Розділ 05: Формотворчі функції---
Glossary/Pow/README Vi (glossary/pow/README-vi.md)
Pow
Lũy thừa.Các phiên bản
glslfloat pow(float x, float y)
vec2 pow(vec2 x, vec2 y)
vec3 pow(vec3 x, vec3 y)
vec4 pow(vec4 x, vec4 y)
Các tham số
`x` cơ số.`y` số mũ.Mô tả
`pow()` trả về kết quả của phép toán `x` mũ `ytext<div class="simpleFunction" data="y = pow(x,3.0); "></div>Tham khảo thêm
inversesqrt, sqrt, Các hàm số cơ bản (Hàm hình dạng - Shape function)
---
Glossary/Precision/README (glossary/precision/README.md)
Precision
Declaration / Example
Parameters
Description
See Also
---
Glossary/Precision/README Ua (glossary/precision/README-ua.md)
precision
Кваліфікатор точності оголошує мінімальний діапазон і точність, які базова реалізація повинна використовувати під час зберігання відповідних змінних.Приклад
glsl
precision lowp float;
precision mediump float;
precision highp float;
precision lowp int;
precision mediump int;
precision highp int;
textЗначення
lowp` — 8+ біт, діапазон для float від -2 до 2, діапазон для int від -2^8 до 2^8
`mediump` — 16+ біт, діапазон для float від -2^14 to +2^14, діапазон для int від -2^10 до 2^10
`highp` — 32+ біт, діапазон для float від -2^62 to +2^62, діапазон для int від -2^16 до 2^16Дивіться також
lowp, mediump, highp---
Glossary/Precision/README Vi (glossary/precision/README-vi.md)
Precision
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Radians/README (glossary/radians/README.md)
Radians
Convert a quantity in degrees to radiansDeclaration
glslfloat radians(float degrees)
vec2 radians(vec2 degrees)
vec3 radians(vec3 degrees)
vec4 radians(vec4 degrees)
Parameters
`degrees` specifies the quantity, in degrees, to be converted to radians.Description
`radians()` converts a quantity, specified in degrees into radians. That is, the return value is `(PI * degrees)/180`.See Also
degrees---
Glossary/Radians/README Ua (glossary/radians/README-ua.md)
radians
Конвертує передане значення у радіаниОголошення
glslfloat radians(float degrees)
vec2 radians(vec2 degrees)
vec3 radians(vec3 degrees)
vec4 radians(vec4 degrees)
Параметри
`degrees` — значення у градусах, яке потрібно перетворити в радіани.Опис
`radians()` перетворює значення, передане в градусах, у радіани.Обрахунок по формулі:
`(PI * degrees) / 180.0textДивіться також
degrees---
Glossary/Radians/README Vi (glossary/radians/README-vi.md)
Radians
Chuyển đổi góc từ đơn vị độ sang radian.Các phiên bản
glsl
float radians(float degrees)
vec2 radians(vec2 degrees)
vec3 radians(vec3 degrees)
vec4 radians(vec4 degrees)
textCác tham số
degrees` góc tính bằng độ, cần quy đổi.Mô tả
`radians()` trả về góc (tính bằng radian) theo công thức `(PI * degrees)/180`.Tham khảo thêm
degrees---
Glossary/Reflect/README (glossary/reflect/README.md)
Reflect
Calculate the reflection direction for an incident vectorDeclaration
glslfloat reflect(float I, float N)
vec2 reflect(vec2 I, vec2 N)
vec3 reflect(vec3 I, vec3 N)
vec4 reflect(vec4 I, vec4 N)
Parameters
`I` specifies the incident vector.`N` specifies the normal vector.Description
For a given incident vector `I` and surface normal `N` reflect returns the reflection direction calculated as `I - 2.0 dot(N, I) N`.`N` should be normalized in order to achieve the desired result.See Also
---
Glossary/Reflect/README Ua (glossary/reflect/README-ua.md)
reflect
Обчислює напрямок відбиття для падаючого вектораОголошення
glslfloat reflect(float I, float N)
vec2 reflect(vec2 I, vec2 N)
vec3 reflect(vec3 I, vec3 N)
vec4 reflect(vec4 I, vec4 N)
Параметри
`I` — вектор інциденту (падаючий вектор).
`N` — вектор нормалі.Опис
Для вектора падіння I і нормалі до поверхні N, reflect повертає напрямок відбиття, обчислений як "`I - 2.0 dot(N, I) N`".
`N` має бути нормалізованим, щоб досягти бажаного результату.Дивіться також
dot(), refract()---
Glossary/Reflect/README Vi (glossary/reflect/README-vi.md)
Reflect
Tính vector phản xạ khi tia tới va chạm với một bề mặt.Các phiên bản
glslfloat reflect(float I, float N)
vec2 reflect(vec2 I, vec2 N)
vec3 reflect(vec3 I, vec3 N)
vec4 reflect(vec4 I, vec4 N)
Các tham số
`I` Vector tới (incident vector), là vector chỉ hướng tia va chạm với bề mặt.`N` Vector pháp tuyến của bề mặt tại điểm va chạm, là vector vuông góc với bề mặt.Mô tả
Cho vector tới `I` và vector pháp tuyến của bề mặt va chạm `N`, vector phản xạ chỉ hướng phản xạ được tính theo công thức `I - 2.0 dot(N, I) N`.`N` nên được chuẩn hóa (normalize) để có kết quả chính xác nhất.Tham khảo thêm
---
Glossary/Refract/README (glossary/refract/README.md)
Refract
Calculate the refraction direction for an incident vectorDeclaration
glslfloat refract(float I, float N, float eta)
vec2 refract(vec2 I, vec2 N, float eta)
vec3 refract(vec3 I, vec3 N, float eta)
vec4 refract(vec4 I, vec4 N, float eta)
Parameters
`I` specifies the incident vector.`N` specifies the normal vector.`eta` specifies the ratio of indices of refraction.Description
For a given incident vector `I`, surface normal `N` and ratio of indices of refraction, `eta`, refract returns the refraction vector, `R`.`R` is calculated as:
glslk = 1.0 - eta eta (1.0 - dot(N, I) * dot(N, I));
if (k < 0.0)
R = genType(0.0); // or genDType(0.0)
else
R = eta I - (eta dot(N, I) + sqrt(k)) * N;
The input parameters `I` and `N` should be normalized in order to achieve the desired result.See Also
---
Glossary/Refract/README Ua (glossary/refract/README-ua.md)
refract
Обчислює напрямок заломлення для падаючого вектораОголошення
glslfloat refract(float I, float N, float eta)
vec2 refract(vec2 I, vec2 N, float eta)
vec3 refract(vec3 I, vec3 N, float eta)
vec4 refract(vec4 I, vec4 N, float eta)
Параметри
`I` — вектор інциденту (падаючий вектор).
`N` — вектор нормалі.
`eta` — співвідношення показників заломлення.Опис
Для вектора падіння I, нормалі до поверхні N та співвідношення показників заломлення eta, refract повертає вектор заломлення R.
`R` обчислюється як:
glslk = 1.0 - eta eta (1.0 - dot(N, I) * dot(N, I));
if (k < 0.0)
R = genType(0.0); // or genDType(0.0)
else
R = eta I - (eta dot(N, I) + sqrt(k)) * N;
Вхідні параметри I та N повинні бути нормалізовані для досягнення бажаного результату.Дивіться також
dot(), reflect()---
Glossary/Refract/README Vi (glossary/refract/README-vi.md)
Refract
Tính vector khúc xạ khi tia tới va chạm với một bề mặt.Các phiên bản
glslfloat refract(float I, float N, float eta)
vec2 refract(vec2 I, vec2 N, float eta)
vec3 refract(vec3 I, vec3 N, float eta)
vec4 refract(vec4 I, vec4 N, float eta)
Các tham số
`I` Vector tới (incident vector), là vector chỉ hướng tia va chạm với bề mặt.`N` Vector pháp tuyến của bề mặt tại điểm va chạm, là vector vuông góc với bề mặt.`eta` Tỉ lệ chiết suất giữa hai môi trường.Mô tả
Cho vector tới `I`, vector pháp tuyến của bề mặt va chạm `N` và , vector khúc xạ chỉ hướng khúc xạ sẽ được tính theo công thức dưới đây và trả về:glslk = 1.0 - eta eta (1.0 - dot(N, I) * dot(N, I));
if (k < 0.0)
R = genType(0.0); // hoặc genDType(0.0)
else
R = eta I - (eta dot(N, I) + sqrt(k)) * N;
`I` và `N` nên được chuẩn hóa (normalize) để có kết quả chính xác nhất.Tham khảo thêm
---
Glossary/Return/README (glossary/return/README.md)
Return
Declaration / Example
Parameters
Description
See Also
---
Glossary/Return/README Ua (glossary/return/README-ua.md)
return
Оператор зупиняє роботу функції та повертає вказане після нього значенняПриклад
glslfloat getSum(float a, float b) {
return a + b;
}
Опис
Оператор return зупиняє роботу функції та повертає вказане після нього обов'язкове значення того типу, який має повертати функція---
Glossary/Return/README Vi (glossary/return/README-vi.md)
Return
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Sampler2D/README (glossary/sampler2D/README.md)
Sampler2D
Declaration / Example
Parameters
Description
See Also
---
Glossary/Sampler2D/README Ua (glossary/sampler2D/README-ua.md)
sampler2D
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/Sampler2D/README Vi (glossary/sampler2D/README-vi.md)
Sampler2D
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/SamplerCube/README (glossary/samplerCube/README.md)
SamplerCube
Declaration / Example
Parameters
Description
See Also
---
Glossary/SamplerCube/README Ua (glossary/samplerCube/README-ua.md)
samplerCube
Оголошення / Приклад
Параметри
Опис
Дивіться також
---
Glossary/SamplerCube/README Vi (glossary/samplerCube/README-vi.md)
SamplerCube
Các phiên bản / Example
Các tham số
Mô tả
Tham khảo thêm
---
Glossary/Sign/README (glossary/sign/README.md)
Sign
Extract the sign of the parameterDeclaration
glslfloat sign(float x)
vec2 sign(vec2 x)
vec3 sign(vec3 x)
vec4 sign(vec4 x)
Parameters
`x` specifies the value from which to extract the sign.Description
`sign()` returns -1.0 if x is less than 0.0, 0.0 if x is equal to 0.0, and +1.0 if x is greater than 0.0.<div class="simpleFunction" data="y = sign(x); "></div>
See Also
abs, Chapter 05: Shaping Functions---
Glossary/Sign/README Ua (glossary/sign/README-ua.md)
sign
Виділяє знак параметраОголошення
glslfloat sign(float x)
vec2 sign(vec2 x)
vec3 sign(vec3 x)
vec4 sign(vec4 x)
Параметри
`x` — значення, з якого слід витягти знак.Опис
`sign()` повертає: -1.0, якщо x менше 0.0; 0.0, якщо x дорівнює 0.0; +1.0, якщо x більше 0.0.<div class="simpleFunction" data="y = sign(x);"></div>
Дивіться також
abs, Розділ 05: Формотворчі функції---
Glossary/Sign/README Vi (glossary/sign/README-vi.md)
Sign
Trả về dấu của một số.Các phiên bản
glslfloat sign(float x)
vec2 sign(vec2 x)
vec3 sign(vec3 x)
vec4 sign(vec4 x)
Các tham số
`x` số cần kiểm tra dấu âm dương.Mô tả
`sign()` trả về -1.0 nếu x < 0.0, 0.0 nếu x = 0.0, và +1.0 nếu x > 0.0.<div class="simpleFunction" data="y = sign(x); "></div>
Tham khảo thêm
abs, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Sin/README (glossary/sin/README.md)
Sin
Return the sine of the parameterDeclaration
glslfloat sin(float angle)
vec2 sin(vec2 angle)
vec3 sin(vec3 angle)
vec4 sin(vec4 angle)
Parameters
`angle` specifies the quantity, in radians, of which to return the sine.Description
`sin()` returns the trigonometric sine of angle.<div class="simpleFunction" data="y = sin(x); "></div>
See Also
acos, cos, asin, tan, atan, Chapter 05: Shaping Functions---
Glossary/Sin/README Pl (glossary/sin/README-pl.md)
Sin
Zwraca sinus podanego argumentuDeklaracja
glslfloat sin(float angle)
vec2 sin(vec2 angle)
vec3 sin(vec3 angle)
vec4 sin(vec4 angle)
Parametry
`angle` określa wartość kąta (w radianach), dla którego ma zostać obliczony sinus.Opis
`sin()` zwraca trygonometryczny sinus podanego kąta.<div class="simpleFunction" data="y = sin(x); "></div>
Zobacz także
acos, cos, asin, tan, atan, Rozdział 05: Shaping Functions---
Glossary/Sin/README Ua (glossary/sin/README-ua.md)
sin
Повертає синус параметраОголошення
glslfloat sin(float angle)
vec2 sin(vec2 angle)
vec3 sin(vec3 angle)
vec4 sin(vec4 angle)
Параметри
`angle` — величина в радіанах, синус якої потрібно повернути.Опис
`sin()` — повертає тригонометричний синус кута.<div class="simpleFunction" data="y = sin(x);"></div>
Дивіться також
acos, cos, asin, tan, atan, Розділ 05: Формотворчі функції---
Glossary/Sin/README Vi (glossary/sin/README-vi.md)
Sin
Hàm sinCác phiên bản
glslfloat sin(float angle)
vec2 sin(vec2 angle)
vec3 sin(vec3 angle)
vec4 sin(vec4 angle)
Các tham số
`angle` góc cần tính sin (theo đơn vị radian)Mô tả
`sin()` trả về kết quả của biểu thức `sin( angle )text<div class="simpleFunction" data="y = sin(x); "></div>Tham khảo thêm
acos, cos, asin, tan, atan, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Smoothstep/README (glossary/smoothstep/README.md)
Smoothstep
Perform Hermite interpolation between two valuesDeclaration
glsl
float smoothstep(float edge0, float edge1, float x)
vec2 smoothstep(vec2 edge0, vec2 edge1, vec2 x)
vec3 smoothstep(vec3 edge0, vec3 edge1, vec3 x)
vec4 smoothstep(vec4 edge0, vec4 edge1, vec4 x)vec2 smoothstep(float edge0, float edge1, vec2 x)
vec3 smoothstep(float edge0, float edge1, vec3 x)
vec4 smoothstep(float edge0, float edge1, vec4 x)
textParameters
edge0` specifies the value of the lower edge of the Hermite function.`edge1` specifies the value of the upper edge of the Hermite function.`x` specifies the source value for interpolation.Description
`smoothstep()` performs smooth Hermite interpolation between `0` and `1` when `edge0 < x < edge1`. This is useful in cases where a threshold function with a smooth transition is desired. `smoothstep()` is equivalent to:
glslgenType t; / Or genDType t; /
t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t t (3.0 - 2.0 * t);
Results are undefined
`if edge0 ≥ edge1`.<div class="simpleFunction" data="y = smoothstep(0.0,1.0,x); "></div>
<div class="codeAndCanvas" data="../05/smoothstep.frag"></div>
See Also
mix, step, Chapter 05: Shaping Functions---
Glossary/Smoothstep/README Vi (glossary/smoothstep/README-vi.md)
Smoothstep
Thực hiện phép nội suy Hermitte giữa 0 và 1 theo tỉ lệ.Các phiên bản
glslfloat smoothstep(float edge0, float edge1, float x)
vec2 smoothstep(vec2 edge0, vec2 edge1, vec2 x)
vec3 smoothstep(vec3 edge0, vec3 edge1, vec3 x)
vec4 smoothstep(vec4 edge0, vec4 edge1, vec4 x)vec2 smoothstep(float edge0, float edge1, vec2 x)
vec3 smoothstep(float edge0, float edge1, vec3 x)
vec4 smoothstep(float edge0, float edge1, vec4 x)
Các tham số
`edge0` cận dưới trên trục hoành của hàm Hermitte`edge1` cận trên trên trục hoành của hàm Hermitte`x` giá trị trên trục hoànhMô tả
`smoothstep()` thực hiện phép nội suy giữa `0` và `1` nếu `edge0 < x < edge1`. Nếu `x < edge0` thì trả về `0`, còn nếu `x > edge1` thì trả về `1`. Hàm này hay dùng khi cần chuyển tiếp giữa 2 vùng mà không muốn đột ngột thay đổi giá trị. Hàm `smoothstep()` tương đương với:
glslgenType t; / Hoặc genDType t; /
t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t t (3.0 - 2.0 * t);
Nếu
`edge0 ≥ edge1` thì kết quả không xác định được.<div class="simpleFunction" data="y = smoothstep(0.0,1.0,x); "></div>
<div class="codeAndCanvas" data="../05/smoothstep.frag"></div>
Tham khảo thêm
mix, step, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Sqrt/README (glossary/sqrt/README.md)
Sqrt
Return the square root of the parameterDeclaration
glslfloat sqrt(float x)
vec2 sqrt(vec2 x)
vec3 sqrt(vec3 x)
vec4 sqrt(vec4 x)
Parameters
`x` specifies the value of which to take the square root.Description
`sqrt()` returns the square root of `x`.<div class="simpleFunction" data="y = sqrt(x); "></div>
<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
See Also
inversesqrt, pow, Chapter 05: Shaping Functions---
Glossary/Sqrt/README Ua (glossary/sqrt/README-ua.md)
sqrt
Повертає значення квадратного кореняОголошення
glslfloat sqrt(float x)
vec2 sqrt(vec2 x)
vec3 sqrt(vec3 x)
vec4 sqrt(vec4 x)
Параметри
`x` — значення, з якого потрібно взяти квадратний корінь.Опис
`sqrt()` повертає квадратний корінь з x.<div class="simpleFunction" data="y = sqrt(x); "></div>
<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
Дивіться також
inversesqrt, pow, Розділ 05: Формотворчі функції---
Glossary/Sqrt/README Vi (glossary/sqrt/README-vi.md)
Sqrt
Tính căn bậc hai của một số.Các phiên bản
glslfloat sqrt(float x)
vec2 sqrt(vec2 x)
vec3 sqrt(vec3 x)
vec4 sqrt(vec4 x)
Các tham số
`x` Số cần tính căn bậc 2.Mô tả
`sqrt()` trả về căn bậc 2 của `x`.<div class="simpleFunction" data="y = sqrt(x); "></div>
<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
Tham khảo thêm
inversesqrt, pow, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Step/README (glossary/step/README.md)
Step
Generate a step function by comparing two valuesDeclaration
glslfloat step(float edge, float x)
vec2 step(vec2 edge, vec2 x)
vec3 step(vec3 edge, vec3 x)
vec4 step(vec4 edge, vec4 x)vec2 step(float edge, vec2 x)
vec3 step(float edge, vec3 x)
vec4 step(float edge, vec4 x)
Parameters
`edge` specifies the location of the edge of the step function.`x` specifies the value to be used to generate the step function.Description
`step()` generates a step function by comparing `x` to `edge`.For element
`i` of the return value, `0.0` is returned `if x[i] < edge[i]`, and `1.0` is returned otherwise.<div class="simpleFunction" data="y = step(0.5,x); "></div>
<div class="codeAndCanvas" data="../05/step.frag"></div>
See Also
mix, smoothstep, Chapter 05: Shaping Functions---
Glossary/Step/README Ua (glossary/step/README-ua.md)
step
Генерує східчасту функцію, порівнюючи два значенняОголошення
glslfloat step(float edge, float x)
vec2 step(vec2 edge, vec2 x)
vec3 step(vec3 edge, vec3 x)
vec4 step(vec4 edge, vec4 x)vec2 step(float edge, vec2 x)
vec3 step(float edge, vec3 x)
vec4 step(float edge, vec4 x)
Параметри
`edge` — порогове значення.
`x` — значення для генерації східчастої функції.Опис
`step()` генерує східчасту функцію шляхом порівняння x із edge.Якщо передане значення
x менше за edge, то функція повертає 0.0, інакше повертається 1.0.<div class="simpleFunction" data="y = step(0.5,x); "></div>
<div class="codeAndCanvas" data="../05/step.frag"></div>
Дивіться також
mix, smoothstep, Розділ 05: Формотворчі функції---
Glossary/Step/README Vi (glossary/step/README-vi.md)
Step
Kiểm tra một biến có nhỏ hơn ngưỡng nhất định không.Các phiên bản
glslfloat step(float edge, float x)
vec2 step(vec2 edge, vec2 x)
vec3 step(vec3 edge, vec3 x)
vec4 step(vec4 edge, vec4 x)vec2 step(float edge, vec2 x)
vec3 step(float edge, vec3 x)
vec4 step(float edge, vec4 x)
Các tham số
`edge` ngưỡng cận biên.`x` giá trị cần kiểm tra xem có nằm trong ngưỡng cận biên không.Mô tả
`step()` so sánh `x` với `edge`.Với mỗi thành phần thứ
`i` trong vector kết quả, nó sẽ nhận giá trị `0.0` nếu `x[i] < edge[i]`, và ngược lại nhận giá trị `1.0`.<div class="simpleFunction" data="y = step(0.5,x); "></div>
<div class="codeAndCanvas" data="../05/step.frag"></div>
Tham khảo thêm
mix, smoothstep, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Struct/README (glossary/struct/README.md)
Struct
Structure variable typeExample
glslstruct matStruct {
vec4 ambientColor;
vec4 diffuseColor;
vec4 specularColor;
float specularExponent;
} newMaterial;newMaterial = matStruct(vec4(0.1, 0.1, 0.1, 1.0),
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.7, 0.7, 0.7, 1.0),
50.0);
Description
`struct` declares a custom data structure based on standard types. A constructor for the structure with the same name is created automatically. The declaration of a variable (in this case "newMaterial") is optional.---
Glossary/Struct/README Vi (glossary/struct/README-vi.md)
Struct
Kiểu dữ liệu cấu trúc - struct.Ví dụ
glslstruct matStruct {
vec4 ambientColor;
vec4 diffuseColor;
vec4 specularColor;
float specularExponent;
} newMaterial;newMaterial = matStruct(vec4(0.1, 0.1, 0.1, 1.0),
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.7, 0.7, 0.7, 1.0),
50.0);
Mô tả
`struct` là một kiểu dữ liệu tự do được tạo nên từ các biến dữ liệu có kiểu cơ bản. Với mỗi struct, một hàm tạo cùng tên sẽ được tạo tự động. Không nhất thiết phải khởi tạo các giá trị khi dùng hàm tạo.---
Glossary/Tan/README (glossary/tan/README.md)
Tan
Return the tangent of the parameterDeclaration
glslfloat tan(float angle)
vec2 tan(vec2 angle)
vec3 tan(vec3 angle)
vec4 tan(vec4 angle)
Parameters
`angle` specifies the quantity, in radians, of which to return the tangent.Description
`tan()` returns the trigonometric tangent of angle.<div class="simpleFunction" data="y = tan(x); "></div>
See Also
cos, acos, sin, asin, atan, Chapter 05: Shaping Functions---
Glossary/Tan/README Ua (glossary/tan/README-ua.md)
tan
Повертає тангенс переданого кутаОголошення
glslfloat tan(float angle)
vec2 tan(vec2 angle)
vec3 tan(vec3 angle)
vec4 tan(vec4 angle)
Параметри
`angle` — величина кута в радіанах, тангенс якого потрібно повернути.Опис
`tan()` повертає тригонометричний тангенс кута.<div class="simpleFunction" data="y = tan(x);"></div>
Дивіться також
cos, acos, sin, asin, atan, Розділ 05: Формотворчі функції---
Glossary/Tan/README Vi (glossary/tan/README-vi.md)
Tan
Tính tan của một góc lượng giác (đơn vị radian)Các phiên bản
glslfloat tan(float angle)
vec2 tan(vec2 angle)
vec3 tan(vec3 angle)
vec4 tan(vec4 angle)
Các tham số
`angle` góc cần tính tan (đơn vị radian)Mô tả
`tan()` tính tan của góc `angletext<div class="simpleFunction" data="y = tan(x); "></div>Tham khảo thêm
cos, acos, sin, asin, atan, Các hàm số cơ bản (Hàm hình dạng - Shape function)---
Glossary/Texture2D/README (glossary/texture2D/README.md)
Texture2D
Retrieves texels from a textureDeclaration
glsl
vec4 texture2D(sampler2D sampler, vec2 coord)
vec4 texture2D(sampler2D sampler, vec2 coord, float bias)
textParameters
sampler` specifies the sampler to which the texture from which texels will be retrieved is bound.`coord` specifies the texture coordinates at which texture will be sampled.`bias` specifies an optional bias to be applied during level-of-detail computation.Description
The texture2D function returns a texel, i.e. the (color) value of the texture for the given coordinates. The function has one input parameter of the type sampler2D and one input parameter of the type `vec2` : sampler, the uniform the texture is bound to, and coord, the 2-dimensional coordinates of the texel to look up.There is an optional third input parameter of the type float: bias. After calculating the appropriate level of detail for a texture with mipmaps the bias is added before the actual texture lookup operation is executed.
Side note: On iOS devices texture lookup functionality is only available in the fragment shader.
See Also
textureCube---
Glossary/Texture2D/README Ua (glossary/texture2D/README-ua.md)
texture2D
Отримує текселі (texels) з текстуриОголошення
glslvec4 texture2D(sampler2D sampler, vec2 coord)
vec4 texture2D(sampler2D sampler, vec2 coord, float bias)
Параметри
`sampler` — семплер, до якого прив'язана текстура, з якої будуть отримані текселі.
`coord` — координати текстури, за якими буде взята текстура.
`bias` — необов'язкове зміщення, яке буде застосовано під час обчислення рівня деталізації.Опис
Функція texture2D повертає тексель, тобто значення (кольору) текстури для заданих координат. Функція має перший вхідний параметр типу sampler2D і другий вхідний параметр типу vec2: семплер, до якої прив'язана текстура, і двовимірні координати текселя.Існує додатковий третій вхідний параметр типу
float. Після обчислення відповідного рівня деталізації для текстури з MIP-картами зміщення додається перед виконанням фактичної операції пошуку текстури.Додаткова примітка: на пристроях iOS функція пошуку текстури доступна лише у фрагментному шейдері.
Дивіться також
textureCube---
Glossary/Texture2D/README Vi (glossary/texture2D/README-vi.md)
Texture2D
Lấy màu của một vị trí bất kỳ trên texture có định dạng cube (6 mặt của khối lập phương).Các phiên bản
glslvec4 texture2D(sampler2D sampler, vec2 coord)
vec4 texture2D(sampler2D sampler, vec2 coord, float bias)
Các tham số
`sampler` là sampler đã khởi tạo với texture, sẽ giúp ta lấy được màu tại tọa độ mong muốn.`coord` tọa độ vị trí cần lấy màu trên texture.`bias` sai số cho phép nếu áp dụng cơ chế Level-Of-Detail.Mô tả
Kết quả của hàm là 1 texel, tức màu tại vị trí chỉ định trên texture. Tham số thứ ba không bắt buộc. Sau khi tính toán xong Level-Of-Detail phù hợp dựa trên các cấp mipmap,
`bias` sẽ được dùng để tìm tọa độ trên texture.Chú ý: Trên các thiết bị iOS, hàm này chỉ có thể dùng trong fragment shader.
Tham khảo thêm
textureCube---
Glossary/TextureCube/README (glossary/textureCube/README.md)
TextureCube
Retrieves texels from a textureDeclaration
glslvec4 textureCube(samplerCube sampler, vec3 coord)
vec4 textureCube(samplerCube sampler, vec3 coord, float bias)
Parameters
`sampler` specifies the sampler to which the texture from which texels will be retrieved is bound.`coord` specifies the texture coordinates at which texture will be sampled.`bias` specifies an optional bias to be applied during level-of-detail computation.Description
The textureCube function returns a texel, i.e. the (color) value of the texture for the given coordinates. The function has one input parameter of the type samplerCube and one input parameter of the type vec3 : sampler, the uniform the texture is bound to, and coord, the 3-dimensional coordinates of the texel to look up.There is an optional third input parameter of the type float: bias. After calculating the appropriate level of detail for a texture with mipmaps the bias is added before the actual texture lookup operation is executed.
Side note: On iOS devices texture lookup functionality is only available in the fragment shader.
See Also
texture2D---
Glossary/TextureCube/README Ua (glossary/textureCube/README-ua.md)
textureCube
Отримує текселі (texels) з текстуриОголошення
glslvec4 textureCube(samplerCube sampler, vec3 coord)
vec4 textureCube(samplerCube sampler, vec3 coord, float bias)
Параметри
`sampler` — семплер, до якого прив'язана текстура, з якої будуть отримані текселі.
`coord` — координати текстури, за якими буде взята текстура.
`bias` — необов'язкове зміщення, яке буде застосовано під час обчислення рівня деталізації.Опис
Функція textureCube повертає тексель, тобто значення (кольору) текстури для заданих координат. Функція має перший вхідний параметр типу samplerCube і другий вхідний параметр типу vec3: семплер, до якої прив'язана текстура, і тривимірні координати текселя.Існує додатковий третій вхідний параметр типу
float. Після обчислення відповідного рівня деталізації для текстури з MIP-картами зміщення додається перед виконанням фактичної операції пошуку текстури.Додаткова примітка: на пристроях iOS функція пошуку текстури доступна лише у фрагментному шейдері.
Дивіться також
texture2D---
Glossary/TextureCube/README Vi (glossary/textureCube/README-vi.md)
TextureCube
Lấy màu của một vị trí bất kỳ trên texture có định dạng cube (6 mặt của khối lập phương).Các phiên bản
glslvec4 textureCube(samplerCube sampler, vec3 coord)
vec4 textureCube(samplerCube sampler, vec3 coord, float bias)
Các tham số
`sampler` là sampler đã khởi tạo với texture, sẽ giúp ta lấy được màu tại tọa độ mong muốn.`coord` tọa độ vị trí cần lấy màu trên texture.`bias` sai số cho phép nếu áp dụng cơ chế Level-Of-Detail.Mô tả
Kết quả của hàm là 1 texel, tức màu tại vị trí chỉ định trên texture. Tham số thứ ba không bắt buộc. Sau khi tính toán xong Level-Of-Detail phù hợp dựa trên các cấp mipmap,
`bias` sẽ được dùng để tìm tọa độ trên texture.Chú ý: Trên các thiết bị iOS, hàm này chỉ có thể dùng trong fragment shader.
Tham khảo thêm
texture2D---
Glossary/Uniform/README (glossary/uniform/README.md)
Uniform
Uniform variable qualifier.Example
glsluniform vec4 direction;
Description
`uniform` variables contain read-only data shared from WebGL/OpenGL environment to a vertex or fragment shader.The value is per primitive, so is useful for variables which remain constant along a primitive, frame or scene.
See Also
attribute, const, varying, Chapter 03: Uniforms---
Glossary/Uniform/README Ua (glossary/uniform/README-ua.md)
uniform
Кваліфікатор уніфікованої змінної.Приклад
glsluniform vec4 direction;
Опис
`uniform`-змінні містять дані лише для читання, які передаються із середовища WebGL/OpenGL у вершинний або фрагментний шейдер.Значення на рівні примітивів, тому вони корисні для змінних, які залишаються постійними вздовж примітива, кадру або сцени.
Дивіться також
attribute, const, varying, Розділ 03: Uniforms---
Glossary/Uniform/README Vi (glossary/uniform/README-vi.md)
Uniform
Qualifier đánh dấu biến chứa dữ liệu được gửi từ CPU sang GPU.Ví dụ
glsluniform vec4 direction;
Mô tả
Các biến `uniform` được gửi từ CPU sang cả 2 chương trình vertex shader và fragment shader chạy trên GPU. Giá trị của các biến này không thể thay đổi._The value is per primitive, so is useful for variables which remain constant along a primitive, frame or scene._
Tham khảo thêm
attribute, const, varying, Chương 03: Uniform---
Glossary/Varying/README (glossary/varying/README.md)
Varying
Varying variable qualifier.Example
glslvarying vec3 position;
Description
`varying` variables contain data shared from a vertex shader to a fragment shader.The variable must be written in the vertex shader and the read-only value in the fragment shader is then interpolated from the vertices which make up the fragment.
See Also
attribute, const, uniform, Chapter 03: Uniforms---
Glossary/Varying/README Ua (glossary/varying/README-ua.md)
varying
Кваліфікатор varying змінної.Приклад
glslvarying vec3 position;
Опис
`varying`-змінні містять дані, які передаються від вершинного шейдера у фрагментний.Змінна має бути спочатку оголошена у вершинному шейдері. У фрагментному шейдері її дані доступні лише для читання і є інтерполяцією з вершин, які складають фрагмент.
Дивіться також
attribute, const, uniform, Розділ 03: Uniforms---
Glossary/Varying/README Vi (glossary/varying/README-vi.md)
Varying
Qualifier đánh dấu biến chứa dữ liệu đã được vertex shader xử lý và chuyển tiếp sang cho fragment shader xử lý tiếp.Ví dụ
glslvarying vec3 position;
Mô tả
`varying` đánh dấu biến chứa dữ liệu đã được vertex shader xử lý và chuyển tiếp sang cho fragment shader xử lý tiếp.Biến này có thể được chỉnh sửa bởi vertex shader, nhưng phải là biến read-only đối với fragment shader.
Tham khảo thêm
attribute, const, uniform, Chương 03: Uniform---
Glossary/Vec2/README (glossary/vec2/README.md)
Vec2
2 dimensional floating point vectorDeclaration
glslvec2 aVec2 = vec2(1.0, 1.0);
vec2 bVec2 = vec2(1.0);vec2 cVec2 = vec2(aVec3);
vec2 dVec2 = vec2(aVec3.x, aVec3.y);
Description
`vec2` is a floating point vector with two components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a vector of higher dimension. The respective values are used to initialize the components.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Vec2/README Ua (glossary/vec2/README-ua.md)
vec2
2-вимірний вектор із float-компонентамиОголошення
glslvec2 aVec2 = vec2(1.0, 1.0);
vec2 bVec2 = vec2(1.0);vec2 cVec2 = vec2(aVec3);
vec2 dVec2 = vec2(aVec3.x, aVec3.y);
Опис
`vec2` — вектор з двома float-компонентами. Способи ініціалізації:- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для обох компонентів;
- надання вектора вищої розмірності, де відповідні значення будуть використані для ініціалізації компонентів;
Дивіться також
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Vec2/README Vi (glossary/vec2/README-vi.md)
Vec2
Vector số nguyên 2 chiều.Các phiên bản
glslvec2 aVec2 = vec2(1.0, 1.0);
vec2 bVec2 = vec2(1.0);vec2 cVec2 = vec2(aVec3);
vec2 dVec2 = vec2(aVec3.x, aVec3.y);
Mô tả
`vec2` vector số nguyên gồm 2 thành phần. Nó có thể được khởi tạo bằng các cách:
- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 2 thành phần
- Lấy giá trị từ 1 vector nhiều chiều hơn. Các thành phần sẽ được khởi tạo theo thứ tự.Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Vec3/README (glossary/vec3/README.md)
Vec3
3 dimensional floating point vectorDeclaration
glslvec3 aVec3 = vec3(1.0, 1.0, 1.0);
vec3 bVec3 = vec3(1.0);vec3 cVec3 = vec3(aVec4);
vec3 dVec3 = vec3(aVec4.x, aVec4.y, aVec4.z);
vec3 eVec3 = vec3(aVec2, aFloat);
vec3 fVec3 = vec3(aVec2.x, aVec2.y, aFloat);
Description
`vec3` is a floating point vector with three components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a vector of higher dimension. The respective values are used to initialize the components.
- Providing a combination of vectors and/or scalars. The respective values are used to initialize the vector. The arguments of the constructor must have at least as many components as the vector that is initialized.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Vec3/README Ua (glossary/vec3/README-ua.md)
vec3
3-вимірний вектор із float-компонентамиОголошення
glslvec3 aVec3 = vec3(1.0, 1.0, 1.0);
vec3 bVec3 = vec3(1.0);vec3 cVec3 = vec3(aVec4);
vec3 dVec3 = vec3(aVec4.x, aVec4.y, aVec4.z);
vec3 eVec3 = vec3(aVec2, aFloat);
vec3 fVec3 = vec3(aVec2.x, aVec2.y, aFloat);
Опис
`vec3` — вектор з трьома float-компонентами. Способи ініціалізації:- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання вектора вищої розмірності, де відповідні значення будуть використані для ініціалізації компонентів;
- надання комбінації векторів та/або скалярів. Для ініціалізації вектора використовуються відповідні значення. Аргументи конструктора повинні містити принаймні стільки ж компонентів, скільки ініціалізований вектор.
Дивіться також
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Vec3/README Vi (glossary/vec3/README-vi.md)
Vec3
Vector số thực 3 chiều.Các phiên bản
glslvec3 aVec3 = vec3(1.0, 1.0, 1.0);
vec3 bVec3 = vec3(1.0);vec3 cVec3 = vec3(aVec4);
vec3 dVec3 = vec3(aVec4.x, aVec4.y, aVec4.z);
vec3 eVec3 = vec3(aVec2, aFloat);
vec3 fVec3 = vec3(aVec2.x, aVec2.y, aFloat);
Mô tả
`vec3` vector số thực gồm 3 thành phần. Nó có thể được khởi tạo bằng các cách:
- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 3 thành phần
- Lấy giá trị từ 1 vector nhiều chiều hơn. Các thành phần sẽ được khởi tạo theo thứ tự.
- Lấy giá trị theo thứ tự lần lượt từ nhiều vector, miễn là đủ 3 thành phần.Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Vec4/README (glossary/vec4/README.md)
Vec4
4 dimensional floating point vectorDeclaration
glslvec4 aVec4 = vec4(1.0, 1.0, 1.0, 1.0);
vec4 bVec4 = vec4(1.0);vec4 cVec4 = vec4(aVec2, aFloat, aVec3);
vec4 dVec4 = vec4(aBvec2.x, aBvec2.y, aFloat, aBvec3.x);
Description
`vec4` is a floating point vector with four components. It can be initialized by:- Providing a scalar value for each component.
- Providing one scalar value. This value is used for all components.
- Providing a combination of vectors and scalars. The respective values are used to initialize the components. The arguments of the constructor must have at least as many components as the vector that is initialized.
See Also
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Vec4/README Vi (glossary/vec4/README-vi.md)
Vec4
Vector số thực 4 chiềuCác phiên bản
glslvec4 aVec4 = vec4(1.0, 1.0, 1.0, 1.0);
vec4 bVec4 = vec4(1.0);vec4 cVec4 = vec4(aVec2, aFloat, aVec3);
vec4 dVec4 = vec4(aBvec2.x, aBvec2.y, aFloat, aBvec3.x);
Mô tả
`vec4` là vector số thực gồm 4 thành phần. Nó có thể được khởi tạo bằng các cách:- Chỉ định giá trị của từng thành phần
- Chỉ định 1 giá trị chung cho cả 4 thành phần
- Lấy giá trị theo thứ tự lần lượt từ nhiều vector, miễn là đủ 4 thành phần.
Tham khảo thêm
bool, int, float, bvec2, bvec3, bvec4, ivec2, ivec3, ivec4, vec2, vec3, vec4, mat2, mat3, mat4---
Glossary/Void/README (glossary/void/README.md)
Void
Declaration
glslvoid main(void);
int aFunction(void);
void bFunction(float);
Description
`void` is used when a function has no parameters or when a function does not return a value.See Also
void, bool, int, float---
Glossary/Void/README Ua (glossary/void/README-ua.md)
void
Приклад
glslvoid main(void);
int aFunction(void);
void bFunction(float);
Опис
`void` — використовується для позначення функції без параметрів або коли вона не повертає значення.Дивіться також
void, bool, int, float---
Glossary/Void/README Vi (glossary/void/README-vi.md)
Void
Các phiên bản
glslvoid main(void);
int aFunction(void);
void bFunction(float);
Mô tả
`void`` is used when a function has no parameters or when a function does not return a value.Tham khảo thêm
void, bool, int, float
---
MotionToolKit/SHORT SUMMARY (motionToolKit/SHORT_SUMMARY.md)
kynd Sep 9, 2016
Designing motion in a fragment shader is not straight forward and can be a bit tedious since it is not an animation tool after all. Here are some convenient tools and examples for controlling easing and timing, drawing shapes, and combining all these to create a nice sequence of motion. The first five examples introduce many useful functions that you can use as building blocks for your design. Following examples demonstrate how you can combine these tool to design complex animations.
---
MotionToolKit/SUMMARY (motionToolKit/SUMMARY.md)
kynd Sep 9, 2016
Designing motion in a fragment shader is not straight forward and can be a bit tedious since it is not an animation tool after all. Here are some convenient tools and examples for controlling easing and timing, drawing shapes, and combining all these to create a nice sequence of motion. The first five examples introduce many useful functions that you can use as building blocks for your design. Following examples demonstrate how you can combine these tool to design complex animations.
Exercises:
* Can you draw different waves using different easing functions, then animate circles following the waves?
* Animate two circles. Can you express difference between different materials through motion? How a metal ball and a plastic ball behave differently?
* Design your character (an animal, monster, spaceship, etc.) by combining shapes and animate it.
* Speed up or down your character animation. Can you make it five times faster or slower than the original?
* Add a new scene to the "wipes" example. How can you make it easy so that you don't have to adjust the timing of all subsequent parts manually?
* The examples here are not necessarily optimized, especially the last one. Try reducing the number of calculation as much as possible. Many functions are running even when the elements they draw are not on-screen. How can you avoid this?
---
ProceduralTexture/SUMMARY (proceduralTexture/SUMMARY.md)
kynd Nov 20, 2016
Shaders are often used to create realistic surfaces of natural or artificial material such as wood, marble, granite, metal, stone, etc. without using photographs or pre-rendered images. Here are demonstrations of some basic techniques. All the examples are based on a number of random and noise functions from Random, Noise, Cellular Noise and Fractal Brownian Motion chapters. Once you get the basic ideas, try tweaking and adding more details to make them more realistic, coming up with new textures and optimizing the performance.
Note that the terrain examples at the bottom use normal map and lighting which are techniques not yet introduced in this book. In short, what they do is to generate a map of the directions of the surface and shade the pixels accordingly. We will cover these ideas in future chapters. Stay tuned.
---
.Github/FUNDING.Yml (.github/FUNDING.yml)
These are supported funding model platforms
github: [patriciogonzalezvivo]
---
.Github/Workflows/Publish.Yml (.github/workflows/publish.yml)
name: Build Book
on:
push:
tags:
- v*
jobs:
build:
runs-on: ubuntu-20.04
env:
DISPLAY: ':99.0'
steps:
- uses: actions/checkout@v2
- name: Set tag release as environment variable
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Step 1 - Install required APT packages
run: |
sudo add-apt-repository ppa:patriciogv/glslviewer-ppa
sudo apt-get update
# Setup the X11
sudo apt-get install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
sudo apt-get install glslviewer texlive-xetex pandoc python2.7
- name: Step 2 - Build books
run: make all
- name: Step 3 - Append version to book files
run: |
mkdir release
mv book.pdf release/book-$RELEASE_VERSION.pdf
mv book.epub release/book-$RELEASE_VERSION.epub
mv book.tex release/book-$RELEASE_VERSION.tex
- name: Step 4 - Release books
uses: fnkr/github-action-ghr@v1
if: startsWith(github.ref, 'refs/tags/')
env:
GHR_PATH: release/
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GHR_REPLACE: true
---