Merge branch 'master' into translation-jp
2
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
.DS_Store
|
||||
.dropbox
|
||||
*Icon*
|
||||
tmp.md
|
||||
tmp*.png
|
||||
book.tex
|
||||
book.pdf
|
||||
log/*
|
||||
|
6
.gitmodules
vendored
@ -1,3 +1,9 @@
|
||||
[submodule "src/parsedown"]
|
||||
path = src/parsedown
|
||||
url = https://github.com/erusev/parsedown.git
|
||||
[submodule "src/glslCanvas"]
|
||||
path = src/glslCanvas
|
||||
url = https://github.com/patriciogonzalezvivo/glslCanvas.git
|
||||
[submodule "src/glslEditor"]
|
||||
path = src/glslEditor
|
||||
url = https://github.com/patriciogonzalezvivo/glslEditor.git
|
||||
|
48
00/README-es.md
Normal file
@ -0,0 +1,48 @@
|
||||
# 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 imágen 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.
|
||||
|
||||
![Imprenta de Gutenberg](gutenpress.jpg)
|
||||
|
||||
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.
|
||||
|
||||
![Journey por That Game Company](journey.jpg)
|
||||
|
||||
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](https://processing.org/) 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 que use OpenGL, OpenGL ES o WebGL. En otras palabras podrás utilizarlo en sketches de [Processing](https://processing.org/), aplicaciones de [openFrameworks](http://openframeworks.cc/), instalaciones interactivas con [Cinder](http://libcinder.org/), incluso en web o en juegos iOS/Android con [Three.js](http://threejs.org/).
|
||||
|
||||
|
||||
## ¿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 imágen, 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 mas grandes que GLSL o el uso fragment shaders. Si estás interesado en aprender openGL/webGL te recomiendo mirar: [OpenGL Introduction](https://open.gl/introduction), [the 8th edition of the OpenGL Programming Guide](http://www.amazon.com/OpenGL-Programming-Guide-Official-Learning/dp/0321773039/ref=sr_1_1?s=books&ie=UTF8&qid=1424007417&sr=1-1&keywords=open+gl+programming+guide) (también conocido como el libro rojo) o [WebGL: Up and Running](http://www.amazon.com/WebGL-Up-Running-Tony-Parisi/dp/144932357X/ref=sr_1_4?s=books&ie=UTF8&qid=1425147254&sr=1-4&keywords=webgl)
|
||||
|
||||
* 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 mátemática empleada en este libro te recomiendo tener cerca alguno de estos libros: [3rd Edition of Mathematics for 3D Game Programming and computer Graphics](http://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/ref=sr_1_1?ie=UTF8&qid=1424007839&sr=8-1&keywords=mathematics+for+games) o [2nd Edition of Essential Mathematics for Games and Interactive Applications](http://www.amazon.com/Essential-Mathematics-Games-Interactive-Applications/dp/0123742978/ref=sr_1_1?ie=UTF8&qid=1424007889&sr=8-1&keywords=essentials+mathematics+for+developers).
|
||||
|
||||
## ¿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 click en "Siguiente" para poder comenzar.
|
||||
|
||||
Alternativamente, dependiendo cuales sean tus necesidades puedes:
|
||||
|
||||
- [Crear una versión offline del libro](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- [Correr los ejemplos en una RaspberryPI sin un navegador](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- [Crear un PDF del libro para imprimir.](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- Usar el [repositorio on-line](https://github.com/patriciogonzalezvivo/thebookofshaders) y ayudarnos a resolver problemas y compartir código.
|
48
00/README-fr.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Introduction
|
||||
|
||||
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
|
||||
|
||||
Les images au-dessus ont été faites de différentes manières. La première a été peinte à la main par Van Gogh, couche après couche. Cela lui a pris des heures. La deuxième a été produite en quelques secondes par la combinaison de quatre matrices de pixels: une pour le cyan, une pour le magenta, une pour le jaune et une pour le noir. La principale différence est que la seconde image n'a pas été produite de manière sérielle (c'est-à-dire non étape par étape, mais tout en même temps).
|
||||
|
||||
Ce livre traite de la technique informatique révolutionnaire, les *fragment shaders*, qui élargissent les possibilités de la génération d'images numériques. Vous pouvez le voir comme l'équivalent de la naissance de l'imprimerie par Gutenberg, mais pour la fabrication d'image.
|
||||
|
||||
![Gutenberg's press](gutenpress.jpg)
|
||||
|
||||
Les fragment shaders vous donnent un contrôle total sur les pixels générés à l'écran et ceci à une très grande vitesse. C'est pourquoi ils sont utilisés dans toutes sortes de cas, depuis les filtres vidéos sur téléphone aux époustouflants jeux vidéos en 3D.
|
||||
|
||||
![Journey by That Game Company](journey.jpg)
|
||||
|
||||
Dans les chapitres suivants, vous allez découvrir à quel point cette technique est incroyablement rapide et puissante et comment l'appliquer à votre travail personnel et professionnel.
|
||||
|
||||
## A qui s'adresse ce livre?
|
||||
|
||||
Ce livre est écrit pour les codeurs créatifs, les développeurs de jeux et ingénieurs qui ont une expérience de la programmation, des connaissances de base en algèbre linéaire et en trigonométrie, et qui veulent pousser leur travail afin d'atteindre un niveau de qualité graphique encore plus palpitant. (Si vous voulez apprendre à programmer, je vous recommande vivement de commencer avec [Processing](https://processing.org/) et de revenir plus tard lorsque vous vous sentirez à l'aise avec la programmation.)
|
||||
|
||||
Ce livre va vous apprendre comment utiliser et intégrer les shaders dans vos projets, en améliorant leur performance et leur qualité graphique. Parce que les shaders GLSL (OpenGL Shading Language) compilent et s'exécutent sur un grand nombre de plateformes, vous serez à même de déployer vos connaissances sur des sketches [Processing](https://processing.org/), des applications [openFrameworks](http://openframeworks.cc/), des installations interactives [Cinder](http://libcinder.org/), des sites avec [Three.js](http://threejs.org/) ou des jeux sous iOS/Android.
|
||||
|
||||
## De quoi traite ce livre?
|
||||
|
||||
Ce livre se concentrera sur l'utilisation des pixel shaders GLSL. Dans un premier temps, nous définirons ce que sont les shaders; puis nous apprendrons comment, grâce à eux, faire des formes procédurales, des motifs, des textures et des animations. Vous apprendrez les bases du langage des shaders et l'appliquerez à des scénarios plus utiles comme: le traitement d'images (les opérations sur les images, les matrices de convolution, les flous, les filtres de couleur, les tables de correspondances et autres effets) et les simulations (le jeu de la vie imaginé par John H. Conway, le système à réaction-diffusion de Gray-Scott, les ondulations à la surface de l'eau, les effets aquarelle, les diagrammes de Voronoï, etc). A la fin de ce livre, nous verrons un ensemble de techniques avancées basées sur le Ray Marching.
|
||||
|
||||
*Dans chaque chapitre, des exemples interactifs vous permettront de vous amuser avec les concepts traités. *Lorsque vous modifierez le code, le résultat de ces changements sera visible instantanément. Certains concepts peuvent être abstaits et déroutants, dans ce cas les exemples interactifs sont les bienvenus pour vous aider à comprendre les informations. Le plus vite vous mettrez ces concepts en mouvement, le plus simple sera votre apprentissage.
|
||||
|
||||
Ce dont ne traite pas ce livre:
|
||||
|
||||
* Ce *n'est pas* un livre sur openGL ou webGL. OpenGL/webGL est un sujet plus vaste que GLSL ou les fragment shaders. Pour en apprendre plus sur OpenGL/webGL, je vous recommande de jeter un oeil sur: [OpenGL Introduction](https://open.gl/introduction), [la 8ème édition du OpenGL Programming Guide](http://www.amazon.com/OpenGL-Programming-Guide-Official-Learning/dp/0321773039/ref=sr_1_1?s=books&ie=UTF8&qid=1424007417&sr=1-1&keywords=open+gl+programming+guide) (aka le livre rouge) ou [WebGL: Up and Running](http://www.amazon.com/WebGL-Up-Running-Tony-Parisi/dp/144932357X/ref=sr_1_4?s=books&ie=UTF8&qid=1425147254&sr=1-4&keywords=webgl)
|
||||
|
||||
* Ce *n'est pas* un livre de maths. Bien que nous couvrirons certains algorithmes et certaines techniques reposant sur la compréhension de l'algèbre et la trigonométrie, nous ne les expliquerons pas en détail. Pour ce qui concerne les mathématiques, je recommande de garder près de vous un des ouvrages suivant: [La 3ème édition de Mathematics for 3D Game Programming and computer Graphics](http://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/ref=sr_1_1?ie=UTF8&qid=1424007839&sr=8-1&keywords=mathematics+for+games) ou [La 2ème édition de Essential Mathematics for Games and Interactive Applications](http://www.amazon.com/Essential-Mathematics-Games-Interactive-Applications/dp/0123742978/ref=sr_1_1?ie=UTF8&qid=1424007889&sr=8-1&keywords=essentials+mathematics+for+developers).
|
||||
|
||||
## De quoi avez-vous besoin pour commencer?
|
||||
|
||||
Pas grand-chose! Si vous avez un navigateur moderne qui supporte WebGL (comme Chrome, Firefox ou Safari) et une connexion internet, cliquez sur le bouton du chapitre suivant en bas de page pour commencer.
|
||||
|
||||
Sinon, au vu de ce que vous avez ou de ce que vous avez besoin pour ce livre, vous pouvez:
|
||||
|
||||
- [Faire une version hors-ligne de ce livre](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- [Exécuter les exemples sur un RaspberryPi sans navigateur](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- [Faire un PDF de ce livre pour impression](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- Utiliser le [répertoire github](https://github.com/patriciogonzalezvivo/thebookofshaders) afin d'aider à résoudre des problèmes et de partager du code.
|
||||
|
48
00/README-kr.md
Normal file
@ -0,0 +1,48 @@
|
||||
# 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*, 라는 디지털 그림 제작과정을 다음 레벨로 이끌어낼 혁신적인 컴퓨팅 기술에 대한 것이다. 바로 그래픽 인더스트리의 구텐베르크 인쇄술인것이다.
|
||||
|
||||
![Gutenberg's press](gutenpress.jpg)
|
||||
|
||||
Fragment shader는 매우 빠른 속도로 스크린에 렌더되는 픽셀들을 완전히 컨트롤 할수 있는 능력을 준다. 핸드폰의 비디오 필터나, AAA 3D 비디오 같은 미디어에서 쓰이는 이유도 이것때문이다.
|
||||
|
||||
![Journey by That Game Company](journey.jpg)
|
||||
|
||||
앞으로 맞이할 챕터들은 독자가 이 강력하고 바른 기술을 스스로 습득하고, 자신의 작업에 적용할수 있는 길을 보여줄것이다.
|
||||
|
||||
## 누구를 위한 책인가?
|
||||
|
||||
이 책은 삼각함수와 선형대수학에 대한 기본이해가 있고 작업에 있어 그래픽 요소를 극대화 시키려는 creative coder, 게임 개발자, 그래픽 엔지니어들을 위한 책이다. (만약 본인이 코딩입문자라면 이 링크를 보고, [Processing](https://processing.org/) 코딩이 익숙해졌을때쯤 다시 돌아와 이책을 보길 권한다.)
|
||||
|
||||
이책은 독자들에게 쉐이더를 프로젝트에 어떻게 쓰는지와, 이를 통한 퍼포먼스 향상과 퀄리티를 높이는 방법들을 보여줄것이다. GLSL (OpenGL Shading Language) 쉐이더들은 OpenGL, OpenGL ES 혹은 WebGL을 가진 환경에서 컴파일되고 실행될것이다. 한마디로, 이것에 대한 기술을 [Processing](https://processing.org/) 스켓치파일들, [openFrameworks](http://openframeworks.cc/) 어플들, [Cinder](http://libcinder.org/) 설치미술들, [Three.js](http://threejs.org/) 웹사이트들 또는 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](https://open.gl/introduction), [the 8th edition of the OpenGL Programming Guide](http://www.amazon.com/OpenGL-Programming-Guide-Official-Learning/dp/0321773039/ref=sr_1_1?s=books&ie=UTF8&qid=1424007417&sr=1-1&keywords=open+gl+programming+guide) (빨간책이라고도 알려진) 이나 [WebGL: Up and Running](http://www.amazon.com/WebGL-Up-Running-Tony-Parisi/dp/144932357X/ref=sr_1_4?s=books&ie=UTF8&qid=1425147254&sr=1-4&keywords=webgl) 등을 추천한다.
|
||||
|
||||
* 이책은 수학색이 아니라. 물론 여러 종류의 알고리즘과 대수학과, 삼각함수에 기댄 수학수식이 많이 이용되지만, 그런것들에 대해 자세히 설명하지는 않을 것이다. 수학에 대한 질문은 : [3rd Edition of Mathematics for 3D Game Programming and computer Graphics](http://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/ref=sr_1_1?ie=UTF8&qid=1424007839&sr=8-1&keywords=mathematics+for+games) 를 보거나, [2nd Edition of Essential Mathematics for Games and Interactive Applications](http://www.amazon.com/Essential-Mathematics-Games-Interactive-Applications/dp/0123742978/ref=sr_1_1?ie=UTF8&qid=1424007889&sr=8-1&keywords=essentials+mathematics+for+developers) 등을 참고하기 바란다.
|
||||
|
||||
## 시작하기 위해 무엇을 해야하나?
|
||||
|
||||
필요한것은 따로 없다! 근대 브라우져를 이용하고 있다면, WebGL이 서포트 될것이고 (구글 크롬이나 파이어폭스, 사파리), 인터넷만 있으면 된다! (지금 보고 있지만) 페이지 최하단에 있는 "Next"버튼을 눌러 다음챕터로 가면 시작이다.
|
||||
|
||||
아니면 , 다음과 같은 방법으로도 이 책을 접할수 있다:
|
||||
|
||||
- [이책의 오프라인 버젼](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- [RaspberryPi에서 브라우져 없이 예제들 돌리기](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- [이책의 PDF버젼 만들기](http://thebookofshaders.com/appendix/)
|
||||
|
||||
- 또는 [온라인 리포](https://github.com/patriciogonzalezvivo/thebookofshaders) 이슈들을 답하거나, 올려주세요.
|
||||
|
0
00/TODO.md
Normal file
BIN
00/cmyk-halftone.png
Normal file
After Width: | Height: | Size: 344 KiB |
48
01/README-es.md
Normal file
@ -0,0 +1,48 @@
|
||||
# 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 mas importante: ¿Qué es un shader?
|
||||
|
||||
![From Letter-by-Letter, Right: William Blades (1891). To Page-by-page, Left: Rolt-Wheeler (1920).](print.png)
|
||||
|
||||
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 imágen 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.
|
||||
|
||||
![Prensa tipográfica china](typepress.jpg)
|
||||
|
||||
## 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 ahi como si fuese una linea de producción. Algunas tareas son mas grandes que otras, esto quiere decir que algunas consumen mas tiempo y energía que el resto. Solemos decir que estas tareas requieren mas 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*.
|
||||
|
||||
![CPU](00.jpeg)
|
||||
|
||||
Los videojuegos y otras aplicaciones gráficas requieren mucho mas 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í esta 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! ¡Si! 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?
|
||||
|
||||
![](03.jpeg)
|
||||
|
||||
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 mas inteligente tener muchos pequeños procesadores funcionando en paralelo al mismo tiempo. Eso es la GPU (Graphic Processor Unit).
|
||||
|
||||
![GPU](04.jpeg)
|
||||
|
||||
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 mas grandes, cuanto mas 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 via hardware, la matemática mas 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 iran 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](https://www.khronos.org/opengl/). 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](http://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**. Mas alla 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 mas sencillos a los casos mas avanzados. Si estás leyendo esto desde un navegador moderno, vas a poder jugar con los ejemplos interactivos. No esperes mas y presiona *Siguiente >>* para ir al próximo capítulo.
|
48
01/README-kr.md
Normal file
@ -0,0 +1,48 @@
|
||||
# 들어가면서
|
||||
## Fragment shader는 무엇인가?
|
||||
|
||||
전 챕터에서 컴퓨터그래픽의 쉐이더는 구텐베르크의 인쇄기술과 같다고 했다. 왜 그런가? 아니, 쉐이더란 도데체 무엇이길래?
|
||||
|
||||
![From Letter-by-Letter, Right: William Blades (1891). To Page-by-page, Left: Rolt-Wheeler (1920).](print.png)
|
||||
|
||||
이미 컴퓨터로 무언가 그리는것에 대한 경험이 조금이라도 있다면, 무언가를 그리기 위해, 어떤 형태의 동그라미, 사각형, 선, 삼각형들과 같은 모양들이 서로 계속 조합해져 최종 원하는 모형이 만들어진다는 것을 이해 할것이다. 이런 프로세스는 어떤 명령어들의 집합이기도 하다고 볼수 있을것이다.
|
||||
|
||||
쉐이더 역시 명령어들의 집합이지만, 이 명령어들이 스크린위에 낱개의 픽셀마다 실행되고 이 실행이 모두 한번에 일어난다는 점이 조금 흥미롭다. 이말은 명령어들은 스크린위에 픽셀의 위치마다 연산이 달라진다는 말이다. 이 명령어 셋들 자체가 스크린 픽셀 포지션을 받고, 색을 출력하는 하나의 프로그램이자 함수인셈이다. 그리고 컴파일된 이 프로그램은 굉장히 빠르다.
|
||||
|
||||
![Chinese movable type](typepress.jpg)
|
||||
|
||||
## 왜 쉐이더는 빠를까?
|
||||
|
||||
대답하기 위해 소개한다. *parallel processing*.
|
||||
|
||||
CPU가 하나의 큰 공장같은 파이프라고 가정하자. 파이프로 들어오는것들은 어떤 작업들이고, 그 작업들이 파이프를 지나면 나오면서 결과가 된다고 하자. 어떤 작업은 크고 복잡하기도 할것이고 어떤건 매우 단순하기도 할것이다. 그리고 컴퓨터는 설계상 절차적으로 작업을 실행하도록 되어 있다; 작업하나가 끝나야 그다음 작업이 시작된다. 근대의 컴퓨터들은 4개의 프로세서로 이루어진 그룹이 이런 파이프처럼 작동하는데, 역시 순차적으로 작업을 하나 끝내고 다음걸 시작하는 방식이다. 각 파이프는 *thread*라고도 알려져 있다.
|
||||
|
||||
![CPU](00.jpeg)
|
||||
|
||||
비디오 게임이나 그래픽응용어플리케이션들은 일반적으로 보다 많은 프로세싱 파워를 요구한다. 이유는 그래픽 요소들 자체가 수많은 픽셀들의 연산으로 이루어졌기 때무이다. 스크린위에 모든 픽셀들이 각각 계산되고, 3D 게임의 경우, 각 3D 객체들과 모든 퍼스펙티브가 계산되어야 할것이다.
|
||||
|
||||
다시 파이프와 작업들의 개념으로 돌아와서, 스크린위에 각 픽셀들은 각각 하나의 작은 작업들을 의미한다. 그리고 이 각각의 작은 작업들은 CPU에게 그다지 버겁지 않은 작업일 것이다. 하지만 (문제 등장) 이렇게 작은 작업들이 스크린위에 픽셀개수만큼 반복되어야 한다는 점이다! 800x600 레졸루션의 게임이라고 하면 480,000개의 픽셀들이 매 프레임 계산되어야 하는데 이말은 14,400,000 번의 계산이 초당 이루어 져야 한다는 말이다! 이것은 꽤나 비효율적일수 있다. (CPU에게) 근대 레티나 디스플레이의 경우, 2880x1800 레졸루션의 픽셀들이 초당 60번 계산되어야 하는데, 계산해보면 초당 311,040,000번이다. 그래픽 엔지니어들은 이 문제를 어떻게 풀까?
|
||||
|
||||
![](03.jpeg)
|
||||
|
||||
바로 이 문제를 풀기위해 등장하는 솔루션이 있다. 바로 parellel processing이다. 강력한 마이크로 프로세서를 몇개 또는 *큰 파이프*를 쓰는대신, 매우 작은 마이크로 프로세서들을 한번에 돌리는 것이다. 그것이 바로 Graphic Processor Unit. GPU이다.
|
||||
|
||||
![GPU](04.jpeg)
|
||||
|
||||
작은 마이크로프로세서들을 여러개의 파이프들로 이루어진 테이블로 생각해보자. 그리고 각 픽셀의 데이터를 핑퐁볼이라고 생각해보자. 14,400,000개의 핑퐁볼이 있다고 한다면, 800x600개의 작은 파이프들로 이루어진 테이블에 480,000개의 핑퐁볼을 30번 떨어뜨리면 아마 최대한 효과적으로 모든 핑퐁볼을 이 테이블 파이프를 통해 통과 시킬수 있을것이다. 하이레졸루션 스크린 렌더링도 이런식으로 진행되는 것이다 - 병렬하드웨어 지원이 높을수록 더 큰 픽셀데이터들을 다룰수 있게 되는것이다.
|
||||
|
||||
GPU의 또다른 슈퍼파워중 하나는, 하드웨어지원으로 가속된 수학 함수 연산이다. 즉, 소프트웨어상에서가 아닌, 마이크로칩상에서 바로 연산이 진행된다는 이야기다. 이말은 더 많은 삼각함수연산, 매트릭스연산이 가능하다는 말이다 - 전기가 얼마나 빠르게 흐르냐에 따라.
|
||||
|
||||
## GLSL은 무엇인가?
|
||||
|
||||
GLSL은 openGL Shading Language의 줄임말로, 앞으로의 챕터들에서 쓰게될 쉐이더 프로그램 랭기지이다. 하드웨어나 운영체제에 따라 이 언어는 달라지기도 한다. 이 책에서는 크로노스그룹에서 규정된 openGL 규격을 따른다. [Khronos Group](https://www.khronos.org/opengl/). OpenGL의 역사를 이해하는것 또한 앞으로 보게될 전문용어들에 크게 도움이 될것이며, 아래 링크를 추천한다: [openglbook.com/chapter-0-preface-what-is-opengl.html](http://openglbook.com/chapter-0-preface-what-is-opengl.html)
|
||||
|
||||
## 왜 쉐이더를 사람들이 어렵다고 할까?
|
||||
|
||||
2차 세계대전에서 쌀공급으로 유명한 미국의 벤삼촌이 이르길, "큰 힘에는 큰 책임이 따른다"고 했다. 병렬연산또한 이 법칙을 따른다; GPU의 강력한 컴퓨테이션 능력은 이것을 쓰기위해 따라야할 제약과 제한이 있다.
|
||||
|
||||
파이프상에서 병렬처리를 하기위해서는, 각 쓰레드마다, 서로에 대해 철저히 개별적이여야 한다. 쓰레드는 다른 쓰레드에 대해 "실명" 되어 있다고 미국에서는 표현하는데, 서로의 데이터에 대해 엑세스가 없다는 말이다. 그래서 각각의 프로세스들은 서로 데이터를 주고 받고 처리하는것이 불가능하다. 쓰레드끼리 소통하게 하는것은 데이터를 더럽힐수 있다.
|
||||
|
||||
또한, GPU는 모든 병렬 마이크로 프로세서들 (파이프들) 을 항시 바쁘게 하고 있다; 이 파이프들은 해당작업이 끝나는 대로 다른 작업을 받아 수행하도록 설계되어 있다. 또한, 각 쓰레드는 이미 수행한 작업에 대한 어떠한 정보도 가질수 없다. 보통 이런 작업들은 운영체제의 UI요소를 그리고 있는것이거나, 게임의 배경화면을 그리거나, 브라우져의 이메일 텍스트를 그리는 것들이다. 각 쓰레드는 서로에게 **실명**되있다고 표현할뿐아니라, **기억이없음** 이라고도 표현하는데, 이것은 이미 수행한 작업에 대한 어떠한 정보도 가지고 있지 않다는 말이다. 바로 이런 점들이 일반적인 프로그래밍 요소와 크게 다른부분이라고 할수 있어서 프로그래밍을 막 접한 이들에게는 어려운 컨셉일수도 있는것이다.
|
||||
|
||||
하지만 걱정하지 마시라! 앞으로의 챕터들에서 우리는 간단한 것부터 복잡한 쉐이딩 연산들에 대해 하나씩 짚고 넘어갈 것이다. 만약 당신이 이 책을 근대 브라우져에서 읽오 있다면, 인터엑티브한 예제들로 인해 공부에 도움을 받을것이다. 더이상 지겨운 서론은 짚어 치우고, *Next >>*를 누르고 코드로 넘어가보자!
|
53
02/README-es.md
Normal file
@ -0,0 +1,53 @@
|
||||
## 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 click 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. Mas 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```. Mas adelante aprenderemos que normalizar valores vuelve mucho mas 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``` (esta definido) y ```#ifndef``` (no esta definido). En nuestro ejemplo, el "Hola mundo", solamente insertamos la segunda línea de código si ```GL_ES``` esta 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 mas 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á:
|
||||
|
||||
```glsl
|
||||
void main() {
|
||||
gl_FragColor = vec4(1,0,0,1); // ERROR
|
||||
}
|
||||
```
|
||||
|
||||
Ahora que ya describimos los elementos mas importantes de nuestro "Hola mundo", es hora de hacer click 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 esta el código que usaríamos para devolver el color rojo:
|
||||
|
||||
```glsl
|
||||
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:
|
||||
|
||||
```glsl
|
||||
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).
|
53
02/README-kr.md
Normal file
@ -0,0 +1,53 @@
|
||||
## Hello World
|
||||
|
||||
보통 "Hello world!" 예제들은 모든 컴퓨터 언어의 시작챕터의 제목이다. 라인하나를 출력하는 명령어로 보통 시작되며, 이 언어가 어떤 것들을 할수 있는지에 대한 첫번째 발걸음을 시작한다.
|
||||
|
||||
GPU-세상에서는 이런 문자열을 출력하는것은 첫번째 발걸음으로 다소 복잡한 경향이 있다. 하여, 우리는 밝은 색하나를 출력하는 것으로 대신해 발걸음을 떼어본다!
|
||||
|
||||
<div class="codeAndCanvas" data="hello_world.frag"></div>
|
||||
|
||||
이첵을 브라우져에서 읽고 있다면, 위에 구간이 상호작용이 가능하다는것을 알수 있다. 클릭하거나, 코드를 수정하여 결과를 바로 볼수 있다는 것이다. 이런 변화 인풋은 바로 GPU파이프라인으로 주입되어 컴파일되고, *실시간으로* 결과를 바꿔준다. 6번째 줄의 코드를 수정하여 이를 체험해보시라.
|
||||
|
||||
매우 간단한 코드지만, 상당히 중요한 내용을 우리는 엿볼수 있는데:
|
||||
|
||||
1. 쉐이더 언어는 color값을 리턴하는 ```main``` 함수이고, 이는 C와 형태가 유사하다.
|
||||
|
||||
2. 마지막으로 리턴되는 픽셀 색 값은, ```gl_FragColor```라는 전역변수에 대입된다.
|
||||
|
||||
3. 이 C-유형의 언어는, 미리 선언된, *변수*들 (```gl_FragColor```같은), *함수* 와 *타입*를 가진다. 이 예제의 경우, 실수로 이루어진 4차원 벡터 타입인, ```vec4```. 더 나아가면서, ```vec3``` 나 ```vec2```, ```float```, ```int```, 또한 ```bool```같은 것들이 보이게 될것이다.
|
||||
|
||||
4. '''vec4''' 타입을 자세히 살펴보면, 각 인수는 RED, GREEN, BLUE and ALPHA 채널이다. 이 값들은 *노멀라이즈된* 값이 ```0.0``` 에서 ```1.0``` 으로 맵핑된것이다. 나중에 나아가면서 노멀라이징을 어떻게 하는지, *map* 을 어떻게 하는지 살펴보게 될것이다.
|
||||
|
||||
5. 예제에서 볼수 있는 또 다른 중요한 *C 요소*는 전처리기 마크로들에 대한 사용이다. 마크로들은 컴파일되기 전에 일어나는 스텝이다. ```#define```을 이용해, 전역변수를 선언하거나, 기본적인 논리구현이 가능하다 (```#ifdef```나 ```#endif```를 통해). 모든 마크로는 해쉬태그 마크 (```#```) 로 시작한다. 이런 Pre-compilation은 컴파일이 실제로 일어나기 바로 전에 각 해쉬태그의 해당값을 이 코드로 카피한다. 위에 예제 'hello world!'를 보면, 우리는 ```GL_ES```를 선언하였는데, 이는 모바일 기기나, 브라우져에서 컴파일될때 전처리되는 부분들인 것이다.
|
||||
|
||||
6. 실수 타입들은 쉐이더에서 신경써야할 부분중 하나인데, *precision(실수정확도)*의 정도가 퍼포먼스에 영향을 미치기 때문이다. 낮은 정도의 precision을 가질수록 빠른 렌더 속도를 가지지만, 렌더 퀄리티가 그만큼 떨어진다는 것을 의미한다. 첫번째 라인을 보면, (```precision mediump float;```) 라는 내용이 보이는데, 실수정확도를 중간정도로 맞춘것이다. 높게는 (```precision lowp float;```) 낮게는 (```precision lowp float;```)이다.
|
||||
|
||||
7. 마지막으로, 그리고 제일 중요할지도 모르지만, GLSL에서는 데이터 타입의 casting이 확보되지 않는다. 무슨말인가하면, 제조업체들은 그래픽카드의 연산을 가속화 하기위해 각각 다른 시도들을 하였는데, 결국은 이것은 최소사양을 저격하고 있다. 자동 casting같은 경우는 여기에 포함되지 않고 있다. 우리의 "hello world!" 예제의 ```vec4``` 또한, ```floats```라는 데이터타입을 예상하게 되는것이다. 앞으로 쉐이더 프로그램을 하면서 실수의 경우에는 항상 점 (```.```) 을 포함하여 이 변수가, 값이 실수임을 명시하자. 예를들어 아래와 같은 코드는 작동이 안될것이다:
|
||||
|
||||
```glsl
|
||||
void main() {
|
||||
gl_FragColor = vec4(1,0,0,1); // ERROR
|
||||
}
|
||||
```
|
||||
|
||||
자, 이제 우리의 첫 예제인 "hello world!"프로그램에 대한 설명을 마친다. 예제의 코드를 수정하면서 이 단원에서 배운 내용을 곱씹어보는 시간을 갖길 바란다. 만약 코드에 에러가 있다면 컴파일 에러가 나면서 화면이 하얗게 될것이다. 아래와 같은 재밌는 시도들도 해볼수 있을것이다.:
|
||||
|
||||
* 실수값들을 정수값들로 바꿔보고, 본인의 그래픽 카드가 이값을 인식하고 아웃풋하는 결과를 보라.
|
||||
|
||||
* 6번째 라인을 코멘트아웃하고, 함수의 픽셀값에 아무런 값도 넣어보지 말라.
|
||||
|
||||
* ```main()```안에, 새로운 함수를 만들고 새로운 값을 리턴하게 해보라, 힌트로는 아래는 빨간색을 리턴하는 함수다 :
|
||||
|
||||
```glsl
|
||||
vec4 red(){
|
||||
return vec4(1.0,0.0,0.0,1.0);
|
||||
}
|
||||
```
|
||||
|
||||
* ```vec4```값을 만드는 방법을 여러가지가 있다. 아래역시 그중하나이다:
|
||||
|
||||
```glsl
|
||||
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);
|
||||
```
|
||||
|
||||
이 예제 자체가 그리 흥미롭지는 않지만 매우 중요한 기본 예제중 하나이다 - 캔바스의 모든 색을 같은색으로 바꿔보는 작업. 다음에 오는 챕터에서는 픽셀 색들을 2개의 인풋을 사용하여 바꿔보는 예제를 해볼것이다. (스크린위에 픽셀들의 위치를 이용한) 그리고 시간을 이용해 이들을 바꾸는 작업도 해볼것이다. (페이지가 로드완료된 후로 몇초가 지났는지를 이용한)
|
BIN
02/hello_world.png
Normal file
After Width: | Height: | Size: 40 KiB |
61
03/README-es.md
Normal file
@ -0,0 +1,61 @@
|
||||
## Uniforms
|
||||
|
||||
Hasta ahora hemos vimos 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.
|
||||
|
||||
```glsl
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
Podemos imaginar que los uniforms son como pequeños puentes entra 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 esta 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](https://www.shadertoy.com/) utiliza las mismas uniforms pero con los siguientes nombres:
|
||||
|
||||
```glsl
|
||||
uniform vec3 iResolution; // viewport resolution (in pixels)
|
||||
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
|
||||
uniform float iGlobalTime; // 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 mas sorpresas. La GPU tiene funciones de ángulo, de trigonometría y exponenciales, que son aceleradas por hardware: [```sin()```](../glossary/?search=sin), [```cos()```](../glossary/?search=cos), [```tan()```](../glossary/?search=tan), [```asin()```](../glossary/?search=asin), [```acos()```](../glossary/?search=acos), [```atan()```](../glossary/?search=atan), [```pow()```](../glossary/?search=pow), [```exp()```](../glossary/?search=exp), [```log()```](../glossary/?search=log), [```sqrt()```](../glossary/?search=sqrt), [```abs()```](../glossary/?search=abs), [```sign()```](../glossary/?search=sign), [```floor()```](../glossary/?search=floor), [```ceil()```](../glossary/?search=ceil), [```fract()```](../glossary/?search=fract), [```mod()```](../glossary/?search=mod), [```min()```](../glossary/?search=min), [```max()```](../glossary/?search=max) y [```clamp()```](../glossary/?search=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 esta 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 mas 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, mas hermoso y gratificante es.
|
||||
|
||||
![](08.png)
|
||||
|
||||
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 estan en pixeles 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 mas 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.
|
63
03/README-kr.md
Normal file
@ -0,0 +1,63 @@
|
||||
## Uniforms
|
||||
|
||||
우리는 여태것 GPU가 병렬처리에 왜 유리한지, 또 GPU의 각 Thread가 한 이미지의 각 부분을 어떻게 다루는지 또한 살펴보았다. 병렬처리 Thread들이 서로에 대해 데이터를 공유할수 없더라도, CPU에서 인풋을 받을수 있다. 그리고 이 인풋들은 모든 Thread들에 있어서 일정(*uniform*)하고 *read only*이다. 즉, 읽을순 있어도 변경할수 없다는 뜻이다.
|
||||
|
||||
이런 인풋들을 ```uniform```이라고 하고, ```float```, ```vec2```, ```vec3```, ```vec4```, ```mat2```, ```mat3```, ```mat4```, ```sampler2D```, ```samplerCube``` 등의 데이터 타입을 지원한다. 유니폼 값들은 보통 floating pont precision설정이 끝난후 선언된다.
|
||||
|
||||
```glsl
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
유니폼은 CPU와 GPU사이에 다리라고 봐도 좋을것이다. 유니폼 값들의 이름은 구현마다 다 다르지만 여기서는: ```u_time``` (쉐이더 연산이 시작된후부터의 초), ```u_resolution``` (쉐이더가 그려지고 있는 빌보드의 사이즈) and ```u_mouse``` (그려지는 빌보드내에서 마우스의 현재 픽셀 위치값) 등으로 나타내겠다. ```u_``` 를 변수앞에 붙혀서, 유니폼이라고 명시한다는 점도 유의하기 바란다. 더 많은 예제는 [ShaderToy.com](https://www.shadertoy.com/) 에서 찾아볼수 있지만, 변수이름이 약간 다르니 살펴보기 바란다:
|
||||
|
||||
```glsl
|
||||
uniform vec3 iResolution; // viewport resolution (in pixels)
|
||||
uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click
|
||||
uniform float iGlobalTime; // shader playback time (in seconds)
|
||||
```
|
||||
|
||||
거두철미하고, 유니폼이 실제로 구현되는 부분을 보자. 아래 코드에서 ```u_time``` - 쉐이더가 구동된후 초 - 를 sine 함수에 인자로 넣어, 빨간색값을 조절하고 있는것을 볼수 있다.
|
||||
|
||||
<div class="codeAndCanvas" data="time.frag"></div>
|
||||
|
||||
GLSL의 재미를 약간 맛볼수 있었다. GPU는 전에도 설명했듯이, hardware accelerated 각연산, 삼각함수연산, 지수함수연산등을 지원한다: [```sin()```](../glossary/?search=sin), [```cos()```](../glossary/?search=cos), [```tan()```](../glossary/?search=tan), [```asin()```](../glossary/?search=asin), [```acos()```](../glossary/?search=acos), [```atan()```](../glossary/?search=atan), [```pow()```](../glossary/?search=pow), [```exp()```](../glossary/?search=exp), [```log()```](../glossary/?search=log), [```sqrt()```](../glossary/?search=sqrt), [```abs()```](../glossary/?search=abs), [```sign()```](../glossary/?search=sign), [```floor()```](../glossary/?search=floor), [```ceil()```](../glossary/?search=ceil), [```fract()```](../glossary/?search=fract), [```mod()```](../glossary/?search=mod), [```min()```](../glossary/?search=min), [```max()```](../glossary/?search=max), [```clamp()```](../glossary/?search=clamp).
|
||||
|
||||
다시 한번 위에 코드를 이용해 놀아보자.
|
||||
|
||||
* 색변화의 속도를 줄여보자.
|
||||
|
||||
* 색이 거의 한개로 보일정도로 빠르게 속도를 높여보자.
|
||||
|
||||
* RGB채널을 직접 조절하고, 속도를 바꾸어 개인만의 패턴을 만들어보자.
|
||||
|
||||
|
||||
## gl_FragCoord
|
||||
|
||||
비슷한 원리로, GLSL은 내장 아웃풋 값들을 가진다. ```vec4 gl_FragColor```, 또한 내장 인풋 값도 있다, *screen fragment*상에서 *pixel*의 위치를 가지고 있는 ```vec4 gl_FragCoord```. ```vec4 gl_FragCoord```로 각 쓰레드가 빌보드의 어느 부분을 작업하고 있는지 알수 있다. 그래서 이값은 ```uniform```값과는 조금다르다. 각 쓰레드마다 값이 다른 *varying*타입이기 때문이다.
|
||||
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>
|
||||
|
||||
위 코드에서, 우리는 빌보드상의 각 픽셀의 위치를 *normalize*했다. 이렇게 함으로 인해서, 값은 ```0.0```에서, ```1.0```사이로 변환되고, 이값은 RED와 GREEN채널에 바로 대입할수 있게 된다.
|
||||
|
||||
아쉽게도, 쉐이더 작업에서 우리는 많은 디버깅 혜택을 볼수가 없다. 그래서, 값을 색에 대입해 예측하고는 한다. 그래서 이 과정을 아래 그림과 같이, 유리병안에 배모형을 조각하는것과 비슷하다고도 한다. 다소 복잡할수 있지만, 그에 비례한 아름다운 결과는 결코 작지 않다.
|
||||
|
||||
![](08.png)
|
||||
|
||||
자 이번에는 여러가지 시도를 해보고 익혀보자.
|
||||
|
||||
* ```(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와 같은 툴을 이용하여.
|
BIN
03/space.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
03/time.png
Normal file
After Width: | Height: | Size: 40 KiB |
159
04/README-es.md
Normal file
@ -0,0 +1,159 @@
|
||||
## 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 el algunos frameworks populares con las mismas uniforms con las que estamos trabajando en este libro. (En el [repositorio de GitHub de este capítulo](https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/04) 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](https://github.com/patriciogonzalezvivo/glslViewer). Este programa corre en MacOS y en RaspberryPI, 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](https://github.com/patriciogonzalezvivo/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](https://twitter.com/mrdoob)) ha estado desarrollando junto con otros [contribuidores](https://github.com/mrdoob/three.js/graphs/contributors) probablemente el framework más conocido de WebGL, llamado [Three.js](http://threejs.org/). 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"```, aqui es donde puedes copiar los ejemplos de este libro.
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script src="js/three.min.js"></script>
|
||||
<script id="vertexShader" type="x-shader/x-vertex">
|
||||
void main() {
|
||||
gl_Position = vec4( position, 1.0 );
|
||||
}
|
||||
</script>
|
||||
<script id="fragmentShader" type="x-shader/x-fragment">
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
|
||||
void main() {
|
||||
vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
gl_FragColor=vec4(st.x,st.y,0.0,1.0);
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var container;
|
||||
var camera, scene, renderer;
|
||||
var uniforms;
|
||||
|
||||
init();
|
||||
animate();
|
||||
|
||||
function init() {
|
||||
container = document.getElementById( 'container' );
|
||||
|
||||
camera = new THREE.Camera();
|
||||
camera.position.z = 1;
|
||||
|
||||
scene = new THREE.Scene();
|
||||
|
||||
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
|
||||
|
||||
uniforms = {
|
||||
u_time: { type: "f", value: 1.0 },
|
||||
u_resolution: { type: "v2", value: new THREE.Vector2() },
|
||||
u_mouse: { type: "v2", value: new THREE.Vector2() }
|
||||
};
|
||||
|
||||
var material = new THREE.ShaderMaterial( {
|
||||
uniforms: uniforms,
|
||||
vertexShader: document.getElementById( 'vertexShader' ).textContent,
|
||||
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
|
||||
} );
|
||||
|
||||
var mesh = new THREE.Mesh( geometry, material );
|
||||
scene.add( mesh );
|
||||
|
||||
renderer = new THREE.WebGLRenderer();
|
||||
renderer.setPixelRatio( window.devicePixelRatio );
|
||||
|
||||
container.appendChild( renderer.domElement );
|
||||
|
||||
onWindowResize();
|
||||
window.addEventListener( 'resize', onWindowResize, false );
|
||||
|
||||
document.onmousemove = function(e){
|
||||
uniforms.u_mouse.value.x = e.pageX
|
||||
uniforms.u_mouse.value.y = e.pageY
|
||||
}
|
||||
}
|
||||
|
||||
function onWindowResize( event ) {
|
||||
renderer.setSize( window.innerWidth, window.innerHeight );
|
||||
uniforms.u_resolution.value.x = renderer.domElement.width;
|
||||
uniforms.u_resolution.value.y = renderer.domElement.height;
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame( animate );
|
||||
render();
|
||||
}
|
||||
|
||||
function render() {
|
||||
uniforms.u_time.value += 0.05;
|
||||
renderer.render( scene, camera );
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
### En **Processing**
|
||||
|
||||
Iniciado por [Ben Fry](http://benfry.com/) y [Casey Reas](http://reas.com/) en 2001, [Processing](https://processing.org/) 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í). [Andres Colubri](https://codeanticode.wordpress.com/) 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.
|
||||
|
||||
```processing
|
||||
PShader 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](https://processing.org/tutorials/pshader/).
|
||||
|
||||
### En **openFrameworks**
|
||||
|
||||
Todos tienen un lugar en el que se sienten mas cómodos, en mi caso, todavía sigue siendo [openFrameworks](http://openframeworks.cc/). 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.
|
||||
|
||||
```cpp
|
||||
void 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 mas información sobre shaders en openFrameworks ve a este [excelente tutorial](http://openframeworks.cc/tutorials/graphics/shaders.html) creado por [Joshua Noble](http://thefactoryfactory.com/).
|
153
04/README-kr.md
Normal file
@ -0,0 +1,153 @@
|
||||
## Running your shader
|
||||
|
||||
이 시점에서, 슬슬 익숙하던 플랫폼에 쉐이딩 기술을 써보고 싶을 것이다. 아래는 사람들이 가장 많이 쓰는 플랫폼들에서 쉐이더를, 또 전 챕터에서 본 uniform 형식을 그대로 쓸수 있는 부분이다. (In the [GitHub repository for this chapter](https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/04), 이 세개의 플랫폼에 대한 소스코드는 여기서 확인할수 있다.)
|
||||
|
||||
**Note 1**: 만약 아래의 프레임워크 외에 것에서 구동하고 싶다면, [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer)를 다운받아서 컴파일할수 있다. 터미널에서 구동되므로, MacOS와 RasberryPi등에서도 구동되며, 이책의 예제들은 모두 최적화 되어 있다.
|
||||
|
||||
**Note 2**: 만약 WebGL에서 쉐이더를 구동하고, 다른 프레임워크를 따로 쓰고 싶지 않다면, [glslCanvas](https://github.com/patriciogonzalezvivo/glslCanvas)를 이용해서 할수 있다. 이 웹 툴은 이 책에 최적화 되어 있고, 실제로 저자가 프로젝트마다 사용하는 툴이기도 하다.
|
||||
|
||||
### **Three.js** 에서
|
||||
|
||||
Ricardo Cabello (aka [MrDoob](https://twitter.com/mrdoob) ) 가 다른 참여자[참여자](https://github.com/mrdoob/three.js/graphs/contributors)들과 개발한 WebGL을 이용한 프레임 워크인 [Three.js](http://threejs.org/). 많은 예제와, 튜토리얼, 책들이 존재하고, 이를 이용해 여러 3D graphics데모를 만들어 볼수 있다.
|
||||
|
||||
아래는 HTML과 JS를 이용해 three.js를 구동하는 예제이다. ```id="fragmentShader"```부분을 보면, 쉐이더가 어디에서 적용되는지 볼수 있다.
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script src="js/three.min.js"></script>
|
||||
<script id="vertexShader" type="x-shader/x-vertex">
|
||||
void main() {
|
||||
gl_Position = vec4( position, 1.0 );
|
||||
}
|
||||
</script>
|
||||
<script id="fragmentShader" type="x-shader/x-fragment">
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
|
||||
void main() {
|
||||
vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
gl_FragColor=vec4(st.x,st.y,0.0,1.0);
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var container;
|
||||
var camera, scene, renderer;
|
||||
var uniforms;
|
||||
|
||||
init();
|
||||
animate();
|
||||
|
||||
function init() {
|
||||
container = document.getElementById( 'container' );
|
||||
|
||||
camera = new THREE.Camera();
|
||||
camera.position.z = 1;
|
||||
|
||||
scene = new THREE.Scene();
|
||||
|
||||
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
|
||||
|
||||
uniforms = {
|
||||
u_time: { type: "f", value: 1.0 },
|
||||
u_resolution: { type: "v2", value: new THREE.Vector2() }
|
||||
};
|
||||
|
||||
var material = new THREE.ShaderMaterial( {
|
||||
uniforms: uniforms,
|
||||
vertexShader: document.getElementById( 'vertexShader' ).textContent,
|
||||
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
|
||||
} );
|
||||
|
||||
var mesh = new THREE.Mesh( geometry, material );
|
||||
scene.add( mesh );
|
||||
|
||||
renderer = new THREE.WebGLRenderer();
|
||||
renderer.setPixelRatio( window.devicePixelRatio );
|
||||
|
||||
container.appendChild( renderer.domElement );
|
||||
|
||||
onWindowResize();
|
||||
window.addEventListener( 'resize', onWindowResize, false );
|
||||
}
|
||||
|
||||
function onWindowResize( event ) {
|
||||
renderer.setSize( window.innerWidth, window.innerHeight );
|
||||
uniforms.u_resolution.value.x = renderer.domElement.width;
|
||||
uniforms.u_resolution.value.y = renderer.domElement.height;
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame( animate );
|
||||
render();
|
||||
}
|
||||
|
||||
function render() {
|
||||
uniforms.u_time.value += 0.05;
|
||||
renderer.render( scene, camera );
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
### **Processing**에서
|
||||
|
||||
[Ben Fry](http://benfry.com/) 와 [Casey Reas](http://reas.com/) 에 의해 2001년에 시작되었고, [Processing](https://processing.org/) 는 간단하고 강력한 환경을 제공하고, 초보자들에게 사랑받는 툴이다. (적어도 저자는 그렇게 생각한다고 한다) [Andres Colubri](https://codeanticode.wordpress.com/) 는 프로세싱에서, openGL부분과 비디오 프로세싱부분에 중요한 업데이트를 했고, 이것으로 인해 GLSL 쉐이더를 구동시키기가 한결 편해졌다. 프로세싱은 ```"shader.frag"```라는 파일을 ```data```폴더에서 찾는다. 책에서 예제를 구동할거라면, 이 폴더에 파일을 rename하여 저장하고 사용하면 된다.
|
||||
|
||||
```processing
|
||||
PShader 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](https://processing.org/tutorials/pshader/)
|
||||
|
||||
### **openFrameworks** 에서
|
||||
|
||||
각자 가장 편한 구동 프레임워크가 있을것인데, 저자의 경우는 [openFrameworks community](http://openframeworks.cc/)이다. C++로 이루어진 OpenGL와 유용한 C++라이브러리들을 wrap한 프레임 워크이고, Processing과 제법 흡사하다. Processing과 마찬가지로, openFrameworks도 data폴더에서 shader파일을 서치한다. ```.frag``` 확장자명을 적는것 또한 까먹지 말자.
|
||||
|
||||
```cpp
|
||||
void 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](http://thefactoryfactory.com/)가 만든 [강좌](http://openframeworks.cc/tutorials/graphics/shaders.html)를 보기 바란다.
|
47
04/README.md
@ -1,10 +1,40 @@
|
||||
## Running your shader
|
||||
|
||||
At this point you're probably excited to try shaders on 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](https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/04), you'll find the full source code for these three frameworks.)
|
||||
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. This tools works consistently across Linux Desktops, MacOS, [RaspberryPi](https://www.raspberrypi.org/) and browsers without the need of changing your code.
|
||||
|
||||
**Note 1**: In case you don't want to try shaders on the following frameworks, but you want to work outside a browser, you can download and compile [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer). This MacOS and RaspberryPi program runs directly from terminal and is especially designed to execute the examples in this book.
|
||||
**Display**: all life-examples in this book are displayed using [glslCanvas](https://github.com/patriciogonzalezvivo/glslCanvas) which makes the process of running standalone shader incredible easy.
|
||||
|
||||
**Note 2**: If you just want to display Shaders using WebGL and you don't care about the frameworks arround it, you can use [glslCanvas](https://github.com/patriciogonzalezvivo/glslCanvas) that exactly do that. This web tool was designed for this book but end up been so usefull that I use it all the time for different projects.
|
||||
```html
|
||||
<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
|
||||
```
|
||||
|
||||
As you can see, it just need to a ```canvas``` element with ```class="glslCanvas"``` and the url to your shader in the ```data-fragment-url```. Learn more about it [here](https://github.com/patriciogonzalezvivo/glslCanvas).
|
||||
|
||||
If you are like me, you will probably want to run shaders directly from the console, in that case you should check out [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer). This application allows you to incorporate shaders into your ```bash``` scripts or unix pipelines and use it in a similarly in a similar way that [ImageMagic](http://www.imagemagick.org/script/index.php). Also [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer) is a great way to compile shaders on your [RaspberryPi](https://www.raspberrypi.org/), reason why [openFrame.io](http://openframe.io/) use it to display shader artwork. Learn more about this application [here](https://github.com/patriciogonzalezvivo/glslViewer).
|
||||
|
||||
```bash
|
||||
glslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -s 1 -o yourOutputImage.png
|
||||
```
|
||||
|
||||
**Create**: in order to illuminate the experience of coding shaders I made an online editor call [glslEditor](https://github.com/patriciogonzalezvivo/glslEditor). This editor is embebed on the book's life 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/](http://editor.thebookofshaders.com/). Learn more about it [here](https://github.com/patriciogonzalezvivo/glslEditor).
|
||||
|
||||
![](glslEditor-01.gif)
|
||||
|
||||
If you prefer to work offline using [SublimeText](https://www.sublimetext.com/) you can install this [package for glslViewer](https://packagecontrol.io/packages/glslViewer). Learn more about it [here](https://github.com/patriciogonzalezvivo/sublime-glslViewer)
|
||||
|
||||
![](glslViewer.gif)
|
||||
|
||||
**Share**: the online editor ([editor.thebookofshaders.com/](http://editor.thebookofshaders.com/)) can share your shaders! Both the embebed and standalone version have an export button where you can get an unique URL's to your shader. Also have the ability to export directly to an [openFrame.io](http://openframe.io/).
|
||||
|
||||
![](glslEditor-00.gif)
|
||||
|
||||
**Curate**: Sharing your code is the beginning of you sharing your shader as artwork! Beside the option to export to [openFrame.io](http://openframe.io/) I made a tool to curate your shaders into a gallery that can be embebed on any site, it’s name is [glslGallery](https://github.com/patriciogonzalezvivo/glslGallery). Learn more [here](https://github.com/patriciogonzalezvivo/glslGallery).
|
||||
|
||||
![](glslGallery.gif)
|
||||
|
||||
## Running your shaders on your favorite framework
|
||||
|
||||
In case you already have experience programming in a framework like: [Processing](https://processing.org/), [Three.js](http://threejs.org/) or [OpenFrameworks](http://openframeworks.cc/), you're probably excited to try shaders on this 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](https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/04), you'll find the full source code for these three frameworks.)
|
||||
|
||||
### In **Three.js**
|
||||
|
||||
@ -50,7 +80,8 @@ Below is an example of the HTML and JS you need to get started with shaders in t
|
||||
|
||||
uniforms = {
|
||||
u_time: { type: "f", value: 1.0 },
|
||||
u_resolution: { type: "v2", value: new THREE.Vector2() }
|
||||
u_resolution: { type: "v2", value: new THREE.Vector2() },
|
||||
u_mouse: { type: "v2", value: new THREE.Vector2() }
|
||||
};
|
||||
|
||||
var material = new THREE.ShaderMaterial( {
|
||||
@ -69,6 +100,11 @@ Below is an example of the HTML and JS you need to get started with shaders in t
|
||||
|
||||
onWindowResize();
|
||||
window.addEventListener( 'resize', onWindowResize, false );
|
||||
|
||||
document.onmousemove = function(e){
|
||||
uniforms.u_mouse.value.x = e.pageX
|
||||
uniforms.u_mouse.value.y = e.pageY
|
||||
}
|
||||
}
|
||||
|
||||
function onWindowResize( event ) {
|
||||
@ -94,7 +130,7 @@ Below is an example of the HTML and JS you need to get started with shaders in t
|
||||
|
||||
Started by [Ben Fry](http://benfry.com/) and [Casey Reas](http://reas.com/) in 2001, [Processing](https://processing.org/) is an extraordinarily simple and powerful environment in which to take your first steps in code (it was for me at least). [Andres Colubri](https://codeanticode.wordpress.com/) 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.
|
||||
|
||||
```processing
|
||||
```cpp
|
||||
PShader shader;
|
||||
|
||||
void setup() {
|
||||
@ -114,6 +150,7 @@ void draw() {
|
||||
```
|
||||
|
||||
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;
|
||||
|
BIN
04/glslEditor-00.gif
Normal file
After Width: | Height: | Size: 2.7 MiB |
BIN
04/glslEditor-01.gif
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
04/glslGallery.gif
Normal file
After Width: | Height: | Size: 848 KiB |
BIN
04/glslViewer.gif
Normal file
After Width: | Height: | Size: 1.6 MiB |
142
05/README-ch.md
Normal file
@ -0,0 +1,142 @@
|
||||
# 算法绘画
|
||||
## 造型函数
|
||||
|
||||
这一章应该叫做宫城先生的粉刷课(来自电影龙威小子的经典桥段)。之前我们把规范化后的 x,y 坐标映射(map)到了红色和绿色通道。本质上说我们是建造了这样一个函数:输入一个二维向量(x,y),然后返回一个四维向量(r,g,b,a)。但在我们跨维度转换数据之前,我们先从更加…更加简单的开始。我们来建一个只有一维变量的函数。你花越多的时间和精力在这上面,你的 shader 功夫就越厉害。
|
||||
|
||||
![The Karate Kid (1984)](mr_miyagi.jpg)
|
||||
|
||||
接下来的代码结构就是我们的基本功。在它之中我们对规范化的 x 坐标(```st.x```)进行可视化。有两种途径:一种是用亮度(度量从黑色到白色的渐变过程),另一种是在顶层绘制一条绿色的线(在这种情况下 x 被直接赋值给 y)。不用过分在意绘制函数,我们马上会更加详细地解释它。
|
||||
|
||||
<div class="codeAndCanvas" data="linear.frag"></div>
|
||||
|
||||
**简注** :```vec3``` 类型构造器“明白”你想要把颜色的三个通道赋值到一个值里,就像 ```vec4``` 明白你想要构建一个四维向量,三维向量加上第四个值(比如颜色的三个值加上透明度)。请参照上面示例的第 20 到 26 行。
|
||||
|
||||
这些代码就是你的基本功;遵守和理解它非常重要。你将会一遍又一遍地回到 0.0 到 1.0 这个区间。你将会掌握融合与构建这些代码的艺术。
|
||||
|
||||
这些 x 与 y(或亮度)之间一对一的关系称作**线性插值**(linear interpolation)。(译者注:插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值。因为对机器运算来说,屏幕像素注定是离散的而不是连续的,计算机图形学常用插值来填充图像变换时像素之间的空隙。)现在起我们可以用一些数学函数来改造这些代码行。比如说我们可以做一个求 x 的 5 次幂的曲线。
|
||||
|
||||
<div class="codeAndCanvas" data="expo.frag"></div>
|
||||
|
||||
很有趣,对吧?试试看把第 19 行的指数改为不同的值,比如:20.0,2.0,1.0,0.0,0.2 或 0.02。理解值和指数之间的关系非常重要。这些数学函数可以让你灵动地控制你的代码,就像是给数据做针灸一样。
|
||||
|
||||
[```pow()```](../glossary/?search=pow) (求x的y次幂)是 GLSL 的一个原生函数,GLSL 有很多原生函数。大多数原生函数都是硬件加速的,也就是说如果你正确使用这些函数,你的代码就会跑得更快。
|
||||
|
||||
换掉第 19 行的幂函数,试试看[```exp()```](../glossary/?search=exp)(以自然常数e为底的指数函数),[```log()```](../glossary/?search=log)(对数函数) 和 [```sqrt()```](../glossary/?search=sqrt)(平方根函数)。当你用 Pi 来玩的时候有些方程会变得更有趣。在第 5 行我定义了一个宏,使得每当程序调用 ```PI``` 的时候就用 ```3.14159265359``` 来替换它。
|
||||
|
||||
### Step 和 Smoothstep
|
||||
|
||||
GLSL 还有一些独特的原生插值函数可以被硬件加速。
|
||||
|
||||
[```step()```](../glossary/?search=step) 插值函数需要输入两个参数。第一个是极限或阀值,第二个是我们想要检测或通过的值。对任何小于阀值的值,返回 ```0.0```,大于阀值,则返回 ```1.0```。
|
||||
|
||||
试试看改变下述代码中第 20 行的值。
|
||||
|
||||
<div class="codeAndCanvas" data="step.frag"></div>
|
||||
|
||||
另一个 GLSL 的特殊函数是 [```smoothstep()```](../glossary/?search=smoothstep)。当给定一个范围的上下限和一个数值,这个函数会在已有的范围内给出插值。前两个参数规定转换的开始和结束点,第三个是给出一个值用来插值。
|
||||
|
||||
<div class="codeAndCanvas" data="smoothstep.frag"></div>
|
||||
|
||||
在之前的例子中,注意第 12 行,我们用到 smoothstep 在 ```plot()``` 函数中画了一条绿色的线。这个函数会对给出的 x 轴上的每个值,在特定的 y 值处制造一个凹凸形变。如何做到呢?通过把两个 [```smoothstep()```](../glossary/?search=smoothstep) 连接到一起。来看看下面这个函数,用它替换上面的第 20 行,把它想成是一个垂直切割。背景看起来很像一条线,不是吗?
|
||||
|
||||
```glsl
|
||||
float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
|
||||
```
|
||||
|
||||
### 正弦和余弦函数
|
||||
|
||||
当你想用数学来制造动效,形态或去混合数值,sin 和 cos 就是你的最佳伙伴。
|
||||
|
||||
这两个基础的三角函数是构造圆的极佳工具,就像张小泉的剪刀一样称手。很重要的一点是你需要知道它们是如何运转的,还有如何把它们结合起来。简单来说,当我们给出一个角度(这里采用弧度制),它就会返回半径为一的圆上一个点的 x 坐标([cos](../glossary/?search=cos))和 y 坐标([sin](../glossary/?search=sin))。正因为 sin 和 cos 返回的是规范化的值(即值域在 -1 和 1 之间),且如此流畅,这就使得它成为一个极其强大的工具。
|
||||
|
||||
![](sincos.gif)
|
||||
|
||||
尽管描述三角函数和圆的关系是一件蛮困难的事情,上图动画很棒地做到了这一点,视觉化展现了它们之间的关系。
|
||||
|
||||
<div class="simpleFunction" data="y = sin(x);"></div>
|
||||
|
||||
仔细看 sin 曲线。观察 y 值是如何平滑地在 +1 和 -1 之间变化。就像之前章节关于的 time 的例子中,你可以用 [```sin()```](../glossary/?search=sin) 的有节奏的变动给其他东西加动效。如果你是在用浏览器阅读的话你可以改动上述公式,看看曲线会如何变动。(注:不要忘记每行最后要加分号!)
|
||||
|
||||
试试下面的小练习,看看会发生什么:
|
||||
|
||||
* 在 ```sin``` 里让 x 加上时间(```u_time```)。让sin 曲线随 x 轴**动起来**。
|
||||
|
||||
* 在 ```sin``` 里用 ```PI``` 乘以 x。注意 sin 曲线上下波动的两部分如何**收缩**了,现在 sin 曲线每两个整数循环一次。
|
||||
|
||||
* 在 ```sin``` 里用时间( ```u_time```)乘以 x。观察各阶段的循环如何变得越来越**频繁**。注意 u_time 可能已经变得非常大,使得图像难以辨认。
|
||||
|
||||
* 给 [```sin(x)```](../glossary/?search=sin)(注意不是 sin 里的 x)加 1.0。观察曲线是如何向上**移动**的,现在值域变成了 0.0 到 2.0。
|
||||
|
||||
* 给 [```sin(x)```](../glossary/?search=sin) 乘以 2.0。观察曲线大小如何**增大**两倍。
|
||||
|
||||
* 计算 ```sin(x)``` 的绝对值([```abs()```](../glossary/?search=abs))。现在它看起来就像一个**弹力球**的轨迹。
|
||||
|
||||
* 只选取 ```sin(x)``` 的小数部分([```fract()```](../glossary/?search=fract))。
|
||||
|
||||
* 使用向正无穷取整([```ceil()```](../glossary/?search=ceil))和向负无穷取整([```floor()```](../glossary/?search=floor)),使得 sin 曲线变成只有 1 和 -1 的电子波。
|
||||
|
||||
### 其他有用的函数
|
||||
|
||||
最后一个练习中我们介绍了一些新函数。现在我们来一个一个试一遍。依次取消注释下列各行,理解这些函数,观察它们是如何运作的。你一定在奇怪……为什么要这么做呢?Google 一下“generative art”(生成艺术)你就知道了。要知道这些函数就是我们的栅栏。我们现在控制的是它在一维中的移动,上上下下。很快,我们就可以尝试二维、三维甚至四维了!
|
||||
|
||||
![Anthony Mattox (2009)](anthony-mattox-ribbon.jpg)
|
||||
|
||||
<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](http://www.flong.com/) 写过关于更加复杂的造型函数的文档,非常有帮助。把它们引入 GLSL 是非常明智的选择,这将是你的代码的广阔的素材库
|
||||
|
||||
* [多项式造型函数(Polynomial Shaping Functions): www.flong.com/texts/code/shapers_poly](http://www.flong.com/texts/code/shapers_poly/)
|
||||
|
||||
* [指数造型函数(Exponential Shaping Functions): www.flong.com/texts/code/shapers_exp](http://www.flong.com/texts/code/shapers_exp/)
|
||||
|
||||
* [圆与椭圆的造型函数(Circular & Elliptical Shaping Functions): www.flong.com/texts/code/shapers_circ](http://www.flong.com/texts/code/shapers_circ/)
|
||||
|
||||
* [贝塞尔和其他参数化造型函数(Bezier and Other Parametric Shaping Functions): www.flong.com/texts/code/shapers_bez](http://www.flong.com/texts/code/shapers_bez/)
|
||||
|
||||
就像厨师自主选择辣椒和各种原料,数字艺术家和创意编程者往往钟情于使用他们自己的造型函数。
|
||||
|
||||
[Iñigo Quiles](http://www.iquilezles.org/) 收集了一套[有用的函数](http://www.iquilezles.org/www/articles/functions/functions.htm)。在看过[这篇文章](http://www.iquilezles.org/www/articles/functions/functions.htm)后,看看下列函数转换到 GLSL 的样子。注意那些细小的改变,比如给浮点数(float)加小数点“.”,给“C 系函数”换成它们在 GLSL 里的名字,比如不是用 ```powf()``` 而是用 ```pow()```:
|
||||
|
||||
* [Impulse](../edit.html#05/impulse.frag)
|
||||
* [Cubic Pulse](../edit.html#05/cubicpulse.frag)
|
||||
* [Exponential Step](../edit.html#05/expstep.frag)
|
||||
* [Parabola](../edit.html#05/parabola.frag)
|
||||
* [Power Curve](../edit.html#05/pcurve.frag)
|
||||
|
||||
给你们看些东西刺激一下斗志,这里有一个非常优雅的例子(作者是 [Danguafer](https://www.shadertoy.com/user/Danguafer),造型函数的空手道黑带)。
|
||||
|
||||
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
|
||||
|
||||
在下一章我们会有一些新的进展。我们会先混合各种颜色,然后画些形状。
|
||||
|
||||
#### 练习
|
||||
|
||||
来看看 [Kynd](http://www.kynd.info/log/) 帮大家制作的公式表。看他如何结合各种函数及它们的属性,始终控制值的范围在 0.0 到 1.0。好了,现在是你自己练习的时候了!来试试这些函数,记住:熟能生巧。
|
||||
|
||||
![Kynd - www.flickr.com/photos/kynd/9546075099/ (2013)](kynd.png)
|
||||
|
||||
#### 填充你的工具箱
|
||||
|
||||
这里有一些工具可以帮你更轻松地可视化这些函数。
|
||||
|
||||
* Grapher:如果你是用 MacOS 系统,用 spotlight 搜 ```grapher``` 就会看到这个超级方便的工具了。
|
||||
|
||||
![OS X Grapher (2004)](grapher.png)
|
||||
|
||||
* [GraphToy](http://www.iquilezles.org/apps/graphtoy/):仍然是 [Iñigo Quilez](http://www.iquilezles.org) 为大家做的工具,用于在 WebGL 中可视化 GLSL 函数。
|
||||
|
||||
![Iñigo Quilez - GraphToy (2010)](graphtoy.png)
|
||||
|
||||
* [Shadershop](http://tobyschachman.com/Shadershop/):这个超级棒的工具是 [Toby Schachman](http://tobyschachman.com/) 的作品。它会以一种极其视觉化和直观的方式教你如何建造复杂的函数。
|
||||
|
||||
![Toby Schachman - Shadershop (2014)](shadershop.png)
|
143
05/README-es.md
Normal file
@ -0,0 +1,143 @@
|
||||
# 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 mas lejos, transformando valores entre dimensiones, necesitamos comenzar con algo sencillo... mucho mas sencillo. Eso significa comprender las funciones unidimensionales. A mayor tiempo y energía que pongas en aprender esto, mejor será tu karate.
|
||||
|
||||
![The Karate Kid (1984)](mr_miyagi.jpg)
|
||||
|
||||
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 20 y la 26 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 19 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()```](../glossary/?search=pow) es una función nativa en GLSL y hay muchas mas. La mayoría de ellas son aceleradas por hardware, lo que significa que, usadas de la forma correcta, harán tu código mucho mas rápido.
|
||||
|
||||
Reemplaza la función de la línea 19. Prueba otras como: [```exp()```](../glossary/?search=exp), [```log()```](../glossary/?search=log) y [```sqrt()```](../glossary/?search=sqrt). Algunas de estas funciones son mucho mas interesantes cuando las usamos con PI. En la línea 5 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.
|
||||
|
||||
La interpolación [```step()```](../glossary/?search=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>
|
||||
|
||||
La otra función única es el [```smoothstep()```](../glossary/?search=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()```](../glossary/?search=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 de parece a una línea ¿No?
|
||||
|
||||
```glsl
|
||||
float 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 mas ú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](../glossary/?search=cos)) e y ([seno](../glossary/?search=sin)) 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.
|
||||
|
||||
![](sincos.gif)
|
||||
|
||||
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()```](../glossary/?search=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 mas y mas. Nota que u_time en un momento pasa a ser un número muy alto y se hace difícil ver el gráfico.
|
||||
|
||||
* Suma 1.0 a [```sin(x)```](../glossary/?search=sin). Observa como toda la onda es **desplazada** hacia arriba y ahora todos los valores van de 0.0 a 2.0.
|
||||
|
||||
* Multiplica [```sin(x)```](../glossary/?search=sin) por 2.0. Mira como la **amplitud** duplica su tamaño.
|
||||
|
||||
* Calcula el valor absoluto ([```abs()```](../glossary/?search=abs)) de ```sin(x)```. Observa como se parece a la trayectoria de una pelota **rebotando**.
|
||||
|
||||
* Extrae sólo la parte fraccionaria ([```fract()```](../glossary/?search=fract)) del resultado de [```sin(x)```](../glossary/?search=sin).
|
||||
|
||||
* Suma el mayor entero mas cercano ([```ceil()```](../glossary/?search=ceil)) y el menor entero mas cercano ([```floor()```](../glossary/?search=floor)) del resultado de [```sin(x)```](../glossary/?search=sin) 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 descomentenando 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 porque. 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!
|
||||
|
||||
![Anthony Mattox (2009)](anthony-mattox-ribbon.jpg)
|
||||
|
||||
<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](http://www.flong.com/) 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/texts/code/shapers_poly](http://www.flong.com/texts/code/shapers_poly/)
|
||||
|
||||
* [Exponential Shaping Functions: www.flong.com/texts/code/shapers_exp](http://www.flong.com/texts/code/shapers_exp/)
|
||||
|
||||
* [Circular & Elliptical Shaping Functions: www.flong.com/texts/code/shapers_circ](http://www.flong.com/texts/code/shapers_circ/)
|
||||
|
||||
* [Bezier and Other Parametric Shaping Functions: www.flong.com/texts/code/shapers_bez](http://www.flong.com/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](http://www.iquilezles.org/) tiene una gran colección de [funciones útiles](http://www.iquilezles.org/www/articles/functions/functions.htm). Después de leer [este artículo](http://www.iquilezles.org/www/articles/functions/functions.htm) 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](../edit.html#05/impulse.frag)
|
||||
* [Cubic Pulse](../edit.html#05/cubicpulse.frag)
|
||||
* [Exponential Step](../edit.html#05/expstep.frag)
|
||||
* [Parabola](../edit.html#05/parabola.frag)
|
||||
* [Power Curve](../edit.html#05/pcurve.frag)
|
||||
|
||||
Para que te mantengas motivado, aqui hay un elegante ejemplo (hecho por [Danguafer](https://www.shadertoy.com/user/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](http://www.kynd.info/log/). 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.
|
||||
|
||||
![Kynd - www.flickr.com/photos/kynd/9546075099/ (2013)](kynd.png)
|
||||
|
||||
#### Para tu caja de herramientas
|
||||
|
||||
Estas son algunas herramientas que te ayudaran a visualizar este tipo de funciones.
|
||||
|
||||
* Grapher: si tienes una computadora con Mac OS, escribe ```grapher``` en tu spotlight y podrás usar esta herramienta super útil.
|
||||
|
||||
![OS X Grapher (2004)](grapher.png)
|
||||
|
||||
* [GraphToy](http://www.iquilezles.org/apps/graphtoy/): una vez mas [Iñigo Quilez](http://www.iquilezles.org) ha creado una herramienta para visualizar funciones GLSL en WebGL.
|
||||
|
||||
![Iñigo Quilez - GraphToy (2010)](graphtoy.png)
|
||||
|
||||
* [Shadershop](http://tobyschachman.com/Shadershop/): esta herramienta increíble creada por [Toby Schachman](http://tobyschachman.com/) te enseñará a consturir funciones complejas de una manera intuitiva y visual.
|
||||
|
||||
![Toby Schachman - Shadershop (2014)](shadershop.png)
|
143
05/README-kr.md
Normal file
@ -0,0 +1,143 @@
|
||||
# Algorithmic drawing
|
||||
## Shaping functions
|
||||
|
||||
이번 챕터는 "미야기씨의 울타리 수업"이라고 표현할수 있겠다. 전에는, 노멀라이즈된 ,*x*와 *y*를 *red*와 *green*채널들에 적용해 보았다.
|
||||
원래는 2차원 벡터(x and y)를 받아, 4차원 벡터(r, g, b and a)를 리턴하는 함수가 좀더 자연스럽니다. 하지만 지금 이런문제를 해결하기 전에, 간단한것부터 시작해보자... 훨씬더 간단한것부터. 이말은 1차원 함수에 대한 정의에서부터 시작한다. 시간과 에너지를 많이 투자하고 배우고 마스터하려고 하다보면, 우리는 더욱더 강한 쉐이더 카라테를 배울수 있을것이다.
|
||||
|
||||
![베스트 키드 (1985)](mr_miyagi.jpg)
|
||||
|
||||
아래 코드는 우리의 울타리가 될것이다. 여기서 우린 *x* 위치정보(```st.x```)의 노멀라이즈된 값을 2가지 방법으로 시각화 하는데: 하나는 밝기로 (검은색에서 흰색으로 잘 묻어져 나가는 색), 다른 하나는 그 위에 초록색 선( *x* 값이 바로 *y* 값에 적용된)으로 나타낼 것이다. 지금은 선을 그리는 함수에 대해 너무 신경쓰지말자. 그부분은 나중에 더 자세히 다루게 될것이다.
|
||||
|
||||
<div class="codeAndCanvas" data="linear.frag"></div>
|
||||
|
||||
**퀵노트**: ```vec3``` 타입 생성자는 유저가 세가지 컬러 채널들을 입력하려는 것을 "이해" 한다. 또한, ```vec4``` 타입생성자역시, 3차원 벡터와, 1개의 실수(보통 alpha나 opacity라고 일컫는)로 이루어진 4차원 벡터를 만들려고 한다는 점 또한 이해한다. 라인 20과 26을 위에 코드에서 찾아보라.
|
||||
|
||||
이 코드는 다시한번 당신의 울타리이다; 받아들이고 이해하는것이 중요하다. 앞으로도 계쏙 이 *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()```](../glossary/?search=pow) 는 GLSL의 내장형 함수이고 GLSL은 이런 수학함수를 많이 제공한다. 거의 모든 함수들은 하드웨어 레벨에서 구동되도록 '가속화' 되어 있다. 다시말해, 이런 이점을 잘 활용하면 굉장히 빠른 코드를 쓸수 있게 된다.
|
||||
|
||||
라인 19의 power 함수를 다른 함수로 교체해보자.: [```exp()```](../glossary/?search=exp), [```log()```](../glossary/?search=log), [```sqrt()```](../glossary/?search=sqrt). 어떤함수들은 인자로 PI값을 넣었을때 재밌는 결과를 내기도 한다. 라인 5에 ```PI```값을 ```3.14159265359```로 define해 둔것을 알수 있다.
|
||||
|
||||
### Step and Smoothstep
|
||||
|
||||
GLSL에는 하드웨어 가속화된 내장 보간법 함수들이 존재한다.
|
||||
|
||||
[```step()```](../glossary/?search=step) 보간법은 2개의 인자를 받는다. 첫번째것은 경계나, 한계점이고, 두번째것은 체크하거나 넘길 값이다. 어떤값이든 이 한계점보다 낮은 값은 ```0```으로 처리되어 리턴될것이고, 한계점보다 높은 값은 ```1.0```으로 처리되어 리턴될것이다.
|
||||
|
||||
라인 20의 한계점 값을 바꾸어 보자.
|
||||
|
||||
<div class="codeAndCanvas" data="step.frag"></div>
|
||||
|
||||
또 하나의 잘 알려진 함수중 하나는 [```smoothstep()```](../glossary/?search=smoothstep)이다. 두값의 레인지를 주고, 그 사이의 값으로 보간시켜주는 방법이다. 처음 두개의 인자는 레인지의 시작점과 끝점이고, 세번째 인자는 두 점사이에서 보간되고 리턴되는 값이다.
|
||||
|
||||
<div class="codeAndCanvas" data="smoothstep.frag"></div>
|
||||
|
||||
전 예제의 라인 12를 보면, smoothstep을 ```plot()```에 사용하여 초록색 선을 그린것을 알수 있다. 각 *x*값에 대한 *y*값을 *bump*시킨 것으로 표현되고 있는데. 실제로 [```smoothstep()```](../glossary/?search=smoothstep) 를 통해 그려진 쉐이프 A를 같은 방법으로 그려진 쉐이프 B에서 그냥 빼준것이다. 아래 예제를 보고 라인 20과 바꿔보아라. ght?
|
||||
|
||||
```glsl
|
||||
float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
|
||||
```
|
||||
|
||||
### Sine과 Cosine 함수
|
||||
|
||||
수학으로 애니메이션, 쉐이프등을 만들거나 섞을때는, sine과 cosine만큼 좋은 친구도 없다.
|
||||
|
||||
이 두개의 삼각함수로 원을 그리는 유명한 예제를 보면 마치 맥가이버의 스위스 군용 나이프와도 같다. 이들이 왜 이렇게 작동되고, 서로 영향을 주는지 확실히 이해를 해볼 필요가 있다. 요약하자면, 이 함수들로 전해지는 인자값(흔히 radians 값)에 따라, 반지름이 1인 동그라미 모양을 그리는 선 위에 모든 *x* ([cosine](../glossary/?search=cos)) 와 *y* ([sine](../glossary/?search=sin))가 나타내어 진다고 볼수 있다. 그리고, 이 값들은 노멀라이즈된 값(-1 에서 1 사이의 값)으로 리턴되는 이 방식은 굉장히 유용한 툴로 알려져 있다.
|
||||
|
||||
![](sincos.gif)
|
||||
|
||||
삼각함수들과 동그라미에 대한 해석등을 전부 다를수는 없지만, 위에 애니메이션을 통해 이들이 할수 있는 일이 얼마나 간단하고 아름다운지 살펴볼수 있다.
|
||||
|
||||
<div class="simpleFunction" data="y = sin(x);"></div>
|
||||
|
||||
전 챕터의 time 예제에서 봤듯이, sine wave를 통해, *y*값이 얼마나 부드럽게 +1에서 -1으로 이동되는지 확인해보라. [```sin()```](../glossary/?search=sin)의 리드믹한 결과값은 애니메이션에 굉장히 유용하다. 브라우져에서 이 예제를 읽고 있다면 코드를 변경해서 변화를 시도해보자. (노트: 끝에 semicolon은 까먹지 말고 붙히자.)
|
||||
|
||||
아래의 예제들을 실행해보고, 어떤일들이 벌어지는지 관찰해보자:
|
||||
|
||||
* ```sin```을 계산하기 전에 time(```u_time```)을 *x* 적용해보자. *x*에 대한 **움직임**을 습득해보자.
|
||||
|
||||
* ```sin```을 계산하기 전에 *x*에 ```PI```를 곱해보자. 반복구간이 **좁혀** 지는것을 확인할수 있다.
|
||||
|
||||
* time (```u_time```) 와 *x*값을 ```sin```에 적용되기 전에 곱해보자. **반복성*이 잦아지는것을 확인할수 있다. u_time이 커지면서 그래프를 점차 읽기 힘들어지게 된다는 것 또한 확인할수 있다.
|
||||
|
||||
* [```sin(x)```](../glossary/?search=sin)에 1.0을 더해보자. 전체 웨이브가 **이동** 되는 것을 확인할수 있다. 이제 모든 값들은 0.0에서 2.0사이로 구간이 바뀌었다.
|
||||
|
||||
* [```sin(x)```](../glossary/?search=sin)에 2.0을 곱해보자. 웨이브의 전체적 **강도**가 2배로 변하는 것을 알수 있다.
|
||||
|
||||
* 절대값([```abs()```](../glossary/?search=abs))을 ```sin(x)```에 적용해보자. 공이 튕기는것 같은 효과를 볼수 있다.
|
||||
|
||||
* 소수점 구간([```fract()```](../glossary/?search=fract))만 취하고, 이를 [```sin(x)```](../glossary/?search=sin)에 적용해보자.
|
||||
|
||||
* 올림 ([```ceil()```](../glossary/?search=ceil))된 수나 내림([```floor()```](../glossary/?search=floor))된 수 역시 [```sin(x)```](../glossary/?search=sin)에 적용해보고, -1에서 1값이 어떻게 리턴되는지 살펴보자.
|
||||
|
||||
### 키타 유용한 함수들
|
||||
|
||||
마지막에 여러 새로운 함수들을 소개했다. 이제 이들을 가지고 하나씩 적용을 해볼차례이다. 하나씩 uncomment를 하면 결과를 확인할수 있을것이다.이들에 대해 샅샅히 파내어 익혀보자. 혹시나 왜그래야 하는지에 대한 의문이 든다면, 좋은 대답중 하나는 "generative art"에 대한 것이다. 이 함수들이 우리의 울타리 하는 것을 생각하자. 우리는 지금 1차원에서 권법을 연마하는 중인것이다. 곧, 2차원, 3차원, 4차원까지 우리는 진입하게 될것이다!
|
||||
|
||||
![Anthony Mattox (2009)](anthony-mattox-ribbon.jpg)
|
||||
|
||||
<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](http://www.flong.com/) 는 제법 복잡하지만 굉장히 유용한 쉐이핑 함수들을 잘 정리해 두었다. 이들을 GLSL로 포팅하는것또한 굉장히 유용한 공부방법일 것이다. 자신만의 쉐이핑 펑션 라이브러리를 개설해보자.
|
||||
|
||||
* [Polynomial Shaping Functions: www.flong.com/texts/code/shapers_poly](http://www.flong.com/texts/code/shapers_poly/)
|
||||
|
||||
* [Exponential Shaping Functions: www.flong.com/texts/code/shapers_exp](http://www.flong.com/texts/code/shapers_exp/)
|
||||
|
||||
* [Circular & Elliptical Shaping Functions: www.flong.com/texts/code/shapers_circ](http://www.flong.com/texts/code/shapers_circ/)
|
||||
|
||||
* [Bezier and Other Parametric Shaping Functions: www.flong.com/texts/code/shapers_bez](http://www.flong.com/texts/code/shapers_bez/)
|
||||
|
||||
요리사가 자신만의 요리재료와 소스 노하우를 모으듯, digital artist들이나, creative coder들 역시 그들만의 쉐이핑 펑션을 만드는 버릇을 가져야 할것이다.
|
||||
|
||||
[Iñigo Quiles](http://www .iquilezles.org/)가 가지고 있는 [useful functions](http://www.iquilezles.org/www/articles/functions/functions.htm)정리는 매우 유용하다. [이 글](http://www.iquilezles.org/www/articles/functions/functions.htm)을 읽고, GLSL로 번역해보는 작업도 해볼수 있다. 조심해야 될 점들은, "."(점)을 실수 뒤에 꼭 넣어야 한다는 점이다. 함수의 이름이 ```powf()``` 에서 ```pow()```등으로 바뀌어 구현되는 점등이다.
|
||||
|
||||
* [Impulse](../edit.html#05/impulse.frag)
|
||||
* [Cubic Pulse](../edit.html#05/cubicpulse.frag)
|
||||
* [Exponential Step](../edit.html#05/expstep.frag)
|
||||
* [Parabola](../edit.html#05/parabola.frag)
|
||||
* [Power Curve](../edit.html#05/pcurve.frag)
|
||||
|
||||
계속 작업이 뜨겁게 진행되는것을 도울수 있게 여기 멋진 작업도 소개해본다. (made by [Danguafer](https://www.shadertoy.com/user/Danguafer))
|
||||
|
||||
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
|
||||
|
||||
*다음 >>* 챕터에서 우리는 새로운 동작에 대해 배우게 될것이다. 색에 조합에 대해 배우고, 쉐이프를 창조하는 작업일것이다.
|
||||
|
||||
#### 연습
|
||||
|
||||
아래의 공식 테이블을 한번 살펴보자. [Kynd](http://www.kynd.info/log/) 수학함수들이 서로 어떻게 섞여지는지 살펴보고, 이 값들이 0.0에서 1.0사이에서 어떻게 바뀌어 가는지 보자. 이제 스스로 함수들을 복제하고 조합해볼 차례이다. 기억하자. 연습을 할수록 당신의 카라테가 늘게 될것이라는 것을.
|
||||
|
||||
![Kynd - www.flickr.com/photos/kynd/9546075099/ (2013)](kynd.png)
|
||||
|
||||
#### 당신의 툴박스를 위해
|
||||
|
||||
수학함수를 쉽게 시각화 할수있는 툴을 소개해보고자 한다.
|
||||
|
||||
* Grapher: 맥북에서 OSX를 쓴다면, ```grapher```를 스팟라이트에서 검색하면 사용할수 있다.
|
||||
|
||||
![OS X Grapher (2004)](grapher.png)
|
||||
|
||||
* [GraphToy](http://www.iquilezles.org/apps/graphtoy/): [Iñigo Quilez](http://www.iquilezles.org)의 GLSL함수들을 WebGL에서 시각화 하는 툴이다.
|
||||
|
||||
![Iñigo Quilez - GraphToy (2010)](graphtoy.png)
|
||||
|
||||
* [Shadershop](http://tobyschachman.com/Shadershop/): [Toby Schachman](http://tobyschachman.com/)가 만든 복잡한 함수들을 만들고, 직관적인 시각화를 할수 있게 해주는 툴중 하나이다.
|
||||
|
||||
![Toby Schachman - Shadershop (2014)](shadershop.png)
|
BIN
05/expo.png
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
05/linear.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
05/smoothstep.png
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
05/step.png
Normal file
After Width: | Height: | Size: 40 KiB |
@ -28,7 +28,7 @@ vector[3] = vector.a = vector.w = vector.q;
|
||||
|
||||
这些指向向量内部变量的不同方式仅仅是设计用来帮助你写出干净代码的术语。着色语言所包含的灵活性为你互换地思考颜色和坐标位置。
|
||||
|
||||
GLSL中向量类型的另一大特点是可以用你需要的任意顺序简单地投射和混合(变量)值。这种能力被(形象地)称为:*鸡尾酒*。
|
||||
GLSL中向量类型的另一大特点是可以用你需要的任意顺序简单地投射和混合(变量)值。这种能力被(形象地)称为:**鸡尾酒**。
|
||||
|
||||
|
||||
```glsl
|
||||
@ -57,7 +57,7 @@ green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue
|
||||
|
||||
### 混合颜色
|
||||
|
||||
现在你了解到如何定义颜色,是时候将先前所学的整合一下了!在GLSL中,有个十分有用的函数:[```mix()```](../glossary/?search=mix),这个函数让你以百分比混合两个值。猜下百分比的取值范围?没错,0到1!完美!在你学习了这些基于栅格运动功夫后,是时候练习一下了!
|
||||
现在你了解到如何定义颜色,是时候将先前所学的整合一下了!在GLSL中,有个十分有用的函数:[```mix()```](../glossary/?search=mix),这个函数让你以百分比混合两个值。猜下百分比的取值范围?没错,0到1!完美!学了这么久的基本功,是时候来用一用了!
|
||||
|
||||
![](mix-f.jpg)
|
||||
|
||||
@ -65,9 +65,9 @@ green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue
|
||||
|
||||
<div class="codeAndCanvas" data="mix.frag"></div>
|
||||
|
||||
试着来show一下你所学到的:
|
||||
试着来 show 一下你所学到的:
|
||||
|
||||
* 给颜色赋予一个有趣的过渡。想想某种特定的感情。哪种颜色更具代表性?他如何产生?又如何褪去?再想想另外的一种感情以及对应的颜色。然后改变上诉代码中的代表这种情感的开始颜色和结束颜色。Robert Penner 开发了一些列流行的计算机动画塑形函数,被称之为平滑函数。你可以研究这些例子并得到启发,但最好你还是自己写一个自己的过度函数。
|
||||
* 给颜色赋予一个有趣的过渡。想想某种特定的感情。哪种颜色更具代表性?他如何产生?又如何褪去?再想想另外的一种感情以及对应的颜色。然后改变上诉代码中的代表这种情感的开始颜色和结束颜色。Robert Penner 开发了一些列流行的计算机动画塑形函数,被称为[缓动函数](http://easings.net/)。你可以研究这些[例子](../edit.html#06/easing.frag)并得到启发,但最好你还是自己写一个自己的缓动函数。
|
||||
|
||||
|
||||
### 玩玩渐变
|
||||
|
140
06/README-es.md
Normal file
@ -0,0 +1,140 @@
|
||||
## Colores
|
||||
|
||||
![Paul Klee - Color Chart (1931)](klee.jpg)
|
||||
|
||||
No hemos tenido oportunidad todavía de hablar sobre los tipos de vectores en GLSL. Antes de ir mas 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.
|
||||
|
||||
```glsl
|
||||
vec3 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.
|
||||
|
||||
```glsl
|
||||
vec4 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 mas 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*.
|
||||
|
||||
```glsl
|
||||
vec3 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
|
||||
```
|
||||
|
||||
#### Para tu caja de herramientas
|
||||
|
||||
Quizás no estás acostumbrado a seleccionar colores usando números - puede ser un poco confuso. Por suerte, hay muchísimos programas que nos hacen este trabajo mucho mas sencillo. Encuentra el que mas se adapte a tus necesidades y úsalo para trabajar con colores en formato vec3 o vec4. Por ejemplo, estos son los templates que uso en [Spectrum](http://www.eigenlogik.com/spectrum/mac):
|
||||
|
||||
```
|
||||
vec3({{rn}},{{gn}},{{bn}})
|
||||
vec4({{rn}},{{gn}},{{bn}},1.0)
|
||||
```
|
||||
|
||||
### 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()```](../glossary/?search=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!
|
||||
|
||||
|
||||
![](mix-f.jpg)
|
||||
|
||||
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](http://easings.net/), puedes usar [este ejemplo](../edit.html#06/easing.frag) como investigación o inspiración, pero los mejores resultados saldrán cuando crees tus propias transiciones.
|
||||
|
||||
### Jugando con gradientes
|
||||
|
||||
La función [```mix()```](../glossary/?search=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```.
|
||||
|
||||
![](mix-vec.jpg)
|
||||
|
||||
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 lineas 26 y 27. Recuerda que con las lineas 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 lineas 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:
|
||||
|
||||
![William Turner - The Fighting Temeraire (1838)](turner.jpg)
|
||||
|
||||
* Crea un gradiente que se parezca a 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 mas allá de usar los canales de rojo, verde y azul.
|
||||
|
||||
[HSB](http://en.wikipedia.org/wiki/HSL_and_HSV) significa Hue (tono), Saturation (saturación) y Brightness (brillo o valor), es una forma útil y mas 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 mas 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()```](../glossary/?search=length) y [```atan(y,x)```](../glossary/?search=atan) (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 mas funciones geométricas además de [```length```](../glossary/?search=length): [```distance()```](../glossary/?search=distance), [```dot()```](../glossary/?search=dot), [```cross```](../glossary/?search=cross), [```normalize()```](../glossary/?search=normalize), [```faceforward()```](../glossary/?search=fraceforward), [```reflect()```](../glossary/?search=reflect) y [```refract()```](../glossary/?search=refract). También GLSL tiene funciones relacionadas a los vectores como: [```lessThan()```](../glossary/?search=lessThan), [```lessThanEqual()```](../glossary/?search=lessThanEqual), [```greaterThan()```](../glossary/?search=greaterThan), [```greaterThanEqual()```](../glossary/?search=greaterThanEqual), [```equal()```](../glossary/?search=equal) y [```notEqual()```](../glossary/?search=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)```](../glossary/?search=atan) 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.
|
||||
|
||||
![William Home Lizars - Red, blue and yellow spectra, with the solar spectrum (1834)](spectrums.jpg)
|
||||
|
||||
* Si miras detenidamente el círculo cromático usado en los goteros de color (ver la imágen 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 encontras la forma de igualarlo para que luzca igual a la siguiente imágen? [Pista: este es un buen momento para usar funciones de forma.]
|
||||
|
||||
![](colorwheel.png)
|
||||
|
||||
#### 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*](http://www.shaderific.com/glsl-qualifiers/#inputqualifier) 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.
|
||||
|
||||
```glsl
|
||||
int 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.
|
@ -27,7 +27,7 @@ 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 interchangably 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 call *swizzle*.
|
||||
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*.
|
||||
|
||||
```glsl
|
||||
vec3 yellow, magenta, green;
|
||||
|
189
06/easing.frag
Normal file
@ -0,0 +1,189 @@
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
#define PI 3.141592653589793
|
||||
#define HALF_PI 1.5707963267948966
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform vec2 u_mouse;
|
||||
uniform float u_time;
|
||||
|
||||
// Robert Penner's easing functions in GLSL
|
||||
// https://github.com/stackgl/glsl-easings
|
||||
float linear(float t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
float exponentialIn(float t) {
|
||||
return t == 0.0 ? t : pow(2.0, 10.0 * (t - 1.0));
|
||||
}
|
||||
|
||||
float exponentialOut(float t) {
|
||||
return t == 1.0 ? t : 1.0 - pow(2.0, -10.0 * t);
|
||||
}
|
||||
|
||||
float exponentialInOut(float t) {
|
||||
return t == 0.0 || t == 1.0
|
||||
? t
|
||||
: t < 0.5
|
||||
? +0.5 * pow(2.0, (20.0 * t) - 10.0)
|
||||
: -0.5 * pow(2.0, 10.0 - (t * 20.0)) + 1.0;
|
||||
}
|
||||
|
||||
float sineIn(float t) {
|
||||
return sin((t - 1.0) * HALF_PI) + 1.0;
|
||||
}
|
||||
|
||||
float sineOut(float t) {
|
||||
return sin(t * HALF_PI);
|
||||
}
|
||||
|
||||
float sineInOut(float t) {
|
||||
return -0.5 * (cos(PI * t) - 1.0);
|
||||
}
|
||||
|
||||
float qinticIn(float t) {
|
||||
return pow(t, 5.0);
|
||||
}
|
||||
|
||||
float qinticOut(float t) {
|
||||
return 1.0 - (pow(t - 1.0, 5.0));
|
||||
}
|
||||
|
||||
float qinticInOut(float t) {
|
||||
return t < 0.5
|
||||
? +16.0 * pow(t, 5.0)
|
||||
: -0.5 * pow(2.0 * t - 2.0, 5.0) + 1.0;
|
||||
}
|
||||
|
||||
float quarticIn(float t) {
|
||||
return pow(t, 4.0);
|
||||
}
|
||||
|
||||
float quarticOut(float t) {
|
||||
return pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;
|
||||
}
|
||||
|
||||
float quarticInOut(float t) {
|
||||
return t < 0.5
|
||||
? +8.0 * pow(t, 4.0)
|
||||
: -8.0 * pow(t - 1.0, 4.0) + 1.0;
|
||||
}
|
||||
|
||||
float quadraticInOut(float t) {
|
||||
float p = 2.0 * t * t;
|
||||
return t < 0.5 ? p : -p + (4.0 * t) - 1.0;
|
||||
}
|
||||
|
||||
float quadraticIn(float t) {
|
||||
return t * t;
|
||||
}
|
||||
|
||||
float quadraticOut(float t) {
|
||||
return -t * (t - 2.0);
|
||||
}
|
||||
|
||||
float cubicIn(float t) {
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
float cubicOut(float t) {
|
||||
float f = t - 1.0;
|
||||
return f * f * f + 1.0;
|
||||
}
|
||||
|
||||
float cubicInOut(float t) {
|
||||
return t < 0.5
|
||||
? 4.0 * t * t * t
|
||||
: 0.5 * pow(2.0 * t - 2.0, 3.0) + 1.0;
|
||||
}
|
||||
|
||||
float elasticIn(float t) {
|
||||
return sin(13.0 * t * HALF_PI) * pow(2.0, 10.0 * (t - 1.0));
|
||||
}
|
||||
|
||||
float elasticOut(float t) {
|
||||
return sin(-13.0 * (t + 1.0) * HALF_PI) * pow(2.0, -10.0 * t) + 1.0;
|
||||
}
|
||||
|
||||
float elasticInOut(float t) {
|
||||
return t < 0.5
|
||||
? 0.5 * sin(+13.0 * HALF_PI * 2.0 * t) * pow(2.0, 10.0 * (2.0 * t - 1.0))
|
||||
: 0.5 * sin(-13.0 * HALF_PI * ((2.0 * t - 1.0) + 1.0)) * pow(2.0, -10.0 * (2.0 * t - 1.0)) + 1.0;
|
||||
}
|
||||
|
||||
float circularIn(float t) {
|
||||
return 1.0 - sqrt(1.0 - t * t);
|
||||
}
|
||||
|
||||
float circularOut(float t) {
|
||||
return sqrt((2.0 - t) * t);
|
||||
}
|
||||
|
||||
float circularInOut(float t) {
|
||||
return t < 0.5
|
||||
? 0.5 * (1.0 - sqrt(1.0 - 4.0 * t * t))
|
||||
: 0.5 * (sqrt((3.0 - 2.0 * t) * (2.0 * t - 1.0)) + 1.0);
|
||||
}
|
||||
|
||||
float bounceOut(float t) {
|
||||
const float a = 4.0 / 11.0;
|
||||
const float b = 8.0 / 11.0;
|
||||
const float c = 9.0 / 10.0;
|
||||
|
||||
const float ca = 4356.0 / 361.0;
|
||||
const float cb = 35442.0 / 1805.0;
|
||||
const float cc = 16061.0 / 1805.0;
|
||||
|
||||
float t2 = t * t;
|
||||
|
||||
return t < a
|
||||
? 7.5625 * t2
|
||||
: t < b
|
||||
? 9.075 * t2 - 9.9 * t + 3.4
|
||||
: t < c
|
||||
? ca * t2 - cb * t + cc
|
||||
: 10.8 * t * t - 20.52 * t + 10.72;
|
||||
}
|
||||
|
||||
float bounceIn(float t) {
|
||||
return 1.0 - bounceOut(1.0 - t);
|
||||
}
|
||||
|
||||
float bounceInOut(float t) {
|
||||
return t < 0.5
|
||||
? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0))
|
||||
: 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5;
|
||||
}
|
||||
|
||||
float backIn(float t) {
|
||||
return pow(t, 3.0) - t * sin(t * PI);
|
||||
}
|
||||
|
||||
float backOut(float t) {
|
||||
float f = 1.0 - t;
|
||||
return 1.0 - (pow(f, 3.0) - f * sin(f * PI));
|
||||
}
|
||||
|
||||
float backInOut(float t) {
|
||||
float f = t < 0.5
|
||||
? 2.0 * t
|
||||
: 1.0 - (2.0 * t - 1.0);
|
||||
|
||||
float g = pow(f, 3.0) - f * sin(f * PI);
|
||||
|
||||
return t < 0.5
|
||||
? 0.5 * g
|
||||
: 0.5 * (1.0 - g) + 0.5;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 colorA = vec3(0.149,0.141,0.912);
|
||||
vec3 colorB = vec3(1.000,0.833,0.224);
|
||||
|
||||
float t = u_time*0.5;
|
||||
float pct = cubicInOut( abs(fract(t)*2.0-1.) );
|
||||
|
||||
gl_FragColor = vec4(vec3(mix(colorA, colorB, pct)),1.0);
|
||||
}
|
BIN
06/gradient.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
06/hsb-colorwheel.png
Normal file
After Width: | Height: | Size: 432 KiB |
BIN
06/hsb.png
Normal file
After Width: | Height: | Size: 221 KiB |
BIN
06/mix.png
Normal file
After Width: | Height: | Size: 40 KiB |
@ -1,21 +1,21 @@
|
||||
## 图形/形状
|
||||
##### 图形和形状在这里会是有一些习惯上的区别。第二版在校正吧!!!
|
||||
## 形状
|
||||
|
||||
|
||||
![Alice Hubbard, Providence, United States, ca. 1892. Photo: Zindman/Freemont.](froebel.jpg)
|
||||
|
||||
终于!我们一直学习的技能就等着这一刻!你已经学习过GLSL的大部分基础,类型和函数。你一遍又一遍的联系你的造型方程。是时候把他们整合起来了。你就是为了这个挑战而来的!在这一章里,你会学习到如何以一种并行处理方式来画简单的图形。
|
||||
终于!我们一直学习的技能就等着这一刻!你已经学习过GLSL的大部分基础,类型和函数。你一遍又一遍的练习你的造型方程。是时候把他们整合起来了。你就是为了这个挑战而来的!在这一章里,你会学习到如何以一种并行处理方式来画简单的图形。
|
||||
|
||||
### 长方形
|
||||
|
||||
想象我们有张数学课上使用的方格纸,而我们的作业是画一个正方形。纸的大小是10 * 10而正方形应该是8 * 8. 你会怎么做?
|
||||
想象我们有张数学课上使用的方格纸,而我们的作业是画一个正方形。纸的大小是10 * 10而正方形应该是8 * 8。你会怎么做?
|
||||
|
||||
![](grid_paper.jpg)
|
||||
|
||||
你是不是会涂满除了第一行第一列和最后一行和最后一列的所有格点?
|
||||
|
||||
这和着色器有什么关系?方格纸上的每个小方形格点就是一个线程(一个像素)。每个格点有它的位置,就想棋盘上的坐标一样。在之前的章节我们将x和y映射到rgb通道,并且我们学习了如何将二维边界限制在0和1之间。我们如何用这些来画一个中心点位于示像屏的中心正方形?
|
||||
这和着色器有什么关系?方格纸上的每个小方形格点就是一个线程(一个像素)。每个格点有它的位置,就想棋盘上的坐标一样。在之前的章节我们将x和y映射到rgb通道,并且我们学习了如何将二维边界限制在0和1之间。我们如何用这些来画一个中心点位于屏幕中心的正方形?
|
||||
|
||||
我们从空间角度来判别的if语句伪代码开始。这个原理和我们思考方格纸的策略异曲同工。
|
||||
我们从空间角度来判别的 if 语句伪代码开始。这个原理和我们思考方格纸的策略异曲同工。
|
||||
|
||||
```glsl
|
||||
if ( (X GREATER THAN 1) AND (Y GREATER THAN 1) )
|
||||
@ -44,7 +44,7 @@ void main(){
|
||||
}
|
||||
```
|
||||
|
||||
step()函数会让没每一个小于0.1的像素变成黑色(vec3(0.0))并将其与的变成白色(vec3(1.0))。左边和底边的“并行”由逻辑运算符AND完成——当x y都为1.0时返回1.0.这就画了两条黑线,一个在画布的底面另一个在左边。
|
||||
step()函数会让没每一个小于0.1的像素变成黑色(vec3(0.0))并将其与的变成白色(vec3(1.0))。```left``` 乘 ```bottom``` 效果相当于逻辑 AND —— 当 x y 都为 1.0 时乘积才能是 1.0。这样做的效果就是画了两条黑线,一个在画布的底边另一个在左边。
|
||||
|
||||
![](rect-01.jpg)
|
||||
|
||||
@ -59,11 +59,11 @@ step()函数会让没每一个小于0.1的像素变成黑色(vec3(0.0)
|
||||
|
||||
<div class="codeAndCanvas" data="rect-making.frag"></div>
|
||||
|
||||
取消21~22行的注释来看看如何转置坐标的同时重复使用step()函数。这样二维向量vec2(0.0,0.0)会被变换到右上角。这就是转置页面和重复过程的数字等价。
|
||||
取消 21~22 行的注释来看看如何转置坐标的同时重复使用 ```step()``` 函数。这样二维向量 vec2(0.0,0.0) 会被变换到右上角。这就是转置页面和重复过程的数字等价。
|
||||
|
||||
![](rect-02.jpg)
|
||||
|
||||
注意在18行和22行,所有的边(宽)都被放大了。等价于这样写:
|
||||
注意在 18 行和 22 行,所有的边(宽)都被放大了。等价于这样写:
|
||||
|
||||
```glsl
|
||||
vec2 bl = step(vec2(0.1),st); // bottom-left
|
||||
@ -71,19 +71,19 @@ step()函数会让没每一个小于0.1的像素变成黑色(vec3(0.0)
|
||||
color = vec3(bl.x * bl.y * tr.x * tr.y);
|
||||
```
|
||||
|
||||
是不是很有趣?这种都是关于运用step()函数、逻辑运算和转置坐标的结合。
|
||||
是不是很有趣?这种都是关于运用 step() 函数、逻辑运算和转置坐标的结合。
|
||||
|
||||
再进行下一个环节之前,挑战下下面的练习:
|
||||
|
||||
* 改变长方形的比例和大小。
|
||||
|
||||
* 用smoothstep()函数代替step()函数,试试在相同的代码下会有什么不同。注意通过改变取值,你可以不仅可以得到模糊边界也可以由漂亮的顺滑边界。
|
||||
* 用 smoothstep() 函数代替 step() 函数,试试在相同的代码下会有什么不同。注意通过改变取值,你可以不仅可以得到模糊边界也可以由漂亮的顺滑边界。
|
||||
|
||||
* 应用floor()做个另外的案例。
|
||||
* 应用 floor() 做个另外的案例。
|
||||
|
||||
* 挑个你最喜欢的应用做成函数,这样未来你可以调用它。并且让它灵活高效。
|
||||
* 挑个你最喜欢的做成函数,这样未来你可以调用它。并且让它灵活高效。
|
||||
|
||||
* 写一个只画长方形边界的函数。
|
||||
* 写一个只画长方形四边的函数。
|
||||
|
||||
* 想一下如何在一个画板上移动并放置不同的长方形?如果你做出来了,试着像[Piet Mondrian](http://en.wikipedia.org/wiki/Piet_Mondrian)一样创作以长方形和色彩的图画。
|
||||
|
||||
@ -97,7 +97,7 @@ step()函数会让没每一个小于0.1的像素变成黑色(vec3(0.0)
|
||||
|
||||
![](compass.jpg)
|
||||
|
||||
将这个过程翻译给shader意味着纸上的每个方形格点都会隐含着问每个像素(线程)是否在圆的区域以内。我们通过计算像素到中心的距离来实现(这个判断)。
|
||||
将这个过程翻译给 shader 意味着纸上的每个方形格点都会隐含着问每个像素(线程)是否在圆的区域以内。我们通过计算像素到中心的距离来实现(这个判断)。
|
||||
|
||||
![](circle.jpg)
|
||||
|
||||
@ -107,9 +107,9 @@ There are several ways to calculate that distance. The easiest one uses the [```
|
||||
|
||||
![](hypotenuse.png)
|
||||
|
||||
你可以使用[```distance()```](../glossary/?search=distance), [```length()```](../glossary/?search=length) 或 [```sqrt()```](../glossary/?search=sqrt)到计算显示屏的中心的距离。下面的代码包含着三个函数,毫无悬念的他们返回相同的结果。
|
||||
你可以使用[```distance()```](../glossary/?search=distance), [```length()```](../glossary/?search=length) 或 [```sqrt()```](../glossary/?search=sqrt)到计算屏幕的中心的距离。下面的代码包含着三个函数,毫无悬念的他们返回相同的结果。
|
||||
|
||||
* 注释和取消注释某行来试试看用不同方式得到相同的结果。
|
||||
* 注释和取消某行的注释来试试看用不同方式得到相同的结果。
|
||||
|
||||
<div class="codeAndCanvas" data="circle-making.frag"></div>
|
||||
|
||||
@ -132,19 +132,19 @@ There are several ways to calculate that distance. The easiest one uses the [```
|
||||
|
||||
来小试下牛刀:
|
||||
|
||||
* 用[```step()```](../glossary/?search=step)函数把所有大于0.5的像素点变成白色,并把小于的变成黑色(0.0)【原文似乎有问题,等于0.5呢?,这就要看step函数的定义了,哈哈哈】
|
||||
* 用[```step()```](../glossary/?search=step)函数把所有大于0.5的像素点变成白色,并把小于的变成黑色(0.0)。
|
||||
|
||||
* 反转前景色和背景色。
|
||||
|
||||
* 调戏下[```smoothstep()```](../glossary/?search=smoothstep)函数,用不同的值来试验着得到一个顺滑的边界的圆。
|
||||
* 调戏下[```smoothstep()```](../glossary/?search=smoothstep)函数,用不同的值来试着做出一个边界顺滑的圆。
|
||||
|
||||
* 一旦遇到令你满意的应用,把他写成一个函数,这样将来就可以调用了。
|
||||
|
||||
* 给这个圆来些缤纷的颜色吧!(圣诞红?不错的主意)
|
||||
* 给这个圆来些缤纷的颜色吧!
|
||||
|
||||
* 再加点动画?一闪一闪亮晶晶?或者是怦然的心跳?(或许你可以从上一章汲取一些灵感)
|
||||
* 再加点动画?一闪一闪亮晶晶?或者是砰砰跳动的心脏?(或许你可以从上一章汲取一些灵感)
|
||||
|
||||
* 让它动起来?能不能移动它并且在同一个市像屏上放置多个圆?
|
||||
* 让它动起来?能不能移动它并且在同一个屏幕上放置多个圆?
|
||||
|
||||
* 如果你结合函数来混合不同的距离场,会发生什么呢?
|
||||
|
||||
@ -189,7 +189,7 @@ pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
|
||||
|
||||
![Robert Mangold - Untitled (2008)](mangold.jpg)
|
||||
|
||||
在关于颜色的章节我们通过如下的方程把每个像素的 *半径* 和 *角度* 笛卡尔坐标映射到极坐标。
|
||||
在关于颜色的章节我们通过如下的方程把每个像素的 **半径** 和 **角度** 笛卡尔坐标映射到极坐标。
|
||||
|
||||
```glsl
|
||||
vec2 pos = vec2(0.5)-st;
|
||||
@ -216,8 +216,8 @@ pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
|
||||
试着:
|
||||
|
||||
* 让这些图形动起来。
|
||||
* 结合不同的造型函数来 *雕刻* 图形,制作诸如花,雪花和齿轮。
|
||||
* 用我们在 *造型函数* 章节的 ```plot()``` 函数画等高线。
|
||||
* 结合不同的造型函数来 **雕刻** 图形,制作诸如花,雪花和齿轮。
|
||||
* 用我们在 **造型函数** 章节的 ```plot()``` 函数画等高线。
|
||||
|
||||
### 整合的魅力
|
||||
|
||||
@ -231,8 +231,8 @@ pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
|
||||
|
||||
* 结合使用 [```min()```](../glossary/?search=min) 和 [```max()```](../glossary/?search=max) 函数混合距离场。
|
||||
|
||||
* 选一个几何logo,永距离场来衍生(生成)出一个。(用距离场画个自己感兴趣的logo,白话!)
|
||||
* 用距离场画个自己感兴趣的logo。
|
||||
|
||||
恭喜!崎岖一路,走来不容易啊!休息下让这些概念沉淀一下吧!当然不是用简单地用Processing来画些什么。在shader的世界里,画图形变得有些别扭,而且适应这种新的编程范式(编程思维模式)会有些精疲力竭。
|
||||
恭喜!你完成了最艰难的部分!休息下让这些概念沉淀一下吧 —— 用Processing 来画简单的形状很容易,但却不到火候。在 shader 的世界里,画形状是很纠结,而且适应这种新的编程范式会有些累人。
|
||||
|
||||
既然现在你知道了如何画图形,我十分肯定你脑袋里已经充满了新的点子。在接下来的章节里你会学习到怎么移动,旋转以及缩放图形。这将使你的创作如虎添翼!
|
||||
既然现在你知道了如何画形状,我十分肯定你脑袋里已经充满了新的点子。在接下来的章节里你会学习到怎么移动,旋转以及缩放图形。这将使你的创作如虎添翼!
|
0
07/TODO.md
Normal file
BIN
07/circle-making.png
Normal file
After Width: | Height: | Size: 184 KiB |
BIN
07/circle.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
07/polar.png
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
07/rect-df.png
Normal file
After Width: | Height: | Size: 346 KiB |
BIN
07/rect-making.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
07/shapes.png
Normal file
After Width: | Height: | Size: 55 KiB |
0
08/TODO.md
Normal file
BIN
08/cross-rotate.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
08/cross-scale.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
08/cross-translate.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
08/matrix.png
Normal file
After Width: | Height: | Size: 581 KiB |
BIN
08/yuv.png
Normal file
After Width: | Height: | Size: 64 KiB |
121
09/README-ch.md
Executable file
@ -0,0 +1,121 @@
|
||||
## Patterns 图案
|
||||
|
||||
因为着色器按一个个像素执行,那么无论你重复一个图形多少次,计算的数量仍然是个常数。
|
||||
|
||||
[ ![Nina Warmerdam - The IMPRINT Project (2013)](warmerdam.jpg) ](../edit.html#09/dots5.frag)
|
||||
|
||||
本章中我们将综合我们目前所学的并应用在画布上。和前几章一样,我们的策略依然基于乘以空间坐标(0到1之间),这样我们的画的在0到1之间的图形就会重复地形成网格。
|
||||
|
||||
*"网格提供一种基于人的直觉发明事物的框架,并且可以颠覆。自然的混沌肌理提供一种对比和秩序的迹象。从早期罗马浴场里的陶瓷图案到几何镶嵌,人们那时候就习惯用网格来点缀他们的生活。"*[*10 PRINT*, Mit Press, (2013)](http://10print.org/)
|
||||
|
||||
首先让我们记住 [```fract()```](../glossary/?search=fract) 函数。它返回一个数的分数部分,本质上是除1的余数([```mod(x,1.0)```](../glossary/?search=mod))。换句话说, [```fract()```](../glossary/?search=fract) 返回小数点后的数。 我们单位化的坐标系变量 (```st```) 已经是 0.0 到 1.0 之间的了。所以像下面这么做并没有必要:
|
||||
|
||||
```glsl
|
||||
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.html#09/diamondtiles.frag"><canvas id="custom" class="canvas" data-fragment-url="diamondtiles.frag" width="520px" height="200px"></canvas></a>
|
||||
|
||||
* 结合多层图案来制作你自己的 [Scottish Tartan Patterns](https://www.google.com/search?q=scottish+patterns+fabric&tbm=isch&tbo=u&source=univ&sa=X&ei=Y1aFVfmfD9P-yQTLuYCIDA&ved=0CB4QsAQ&biw=1399&bih=799#tbm=isch&q=Scottish+Tartans+Patterns).
|
||||
|
||||
[ ![Vector Pattern Scottish Tartan By Kavalenkava](tartan.jpg) ](http://graphicriver.net/item/vector-pattern-scottish-tartan/6590076)
|
||||
|
||||
### 偏移图案
|
||||
|
||||
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方向上偏移了一半砖的长度,没隔一行偏移一次。我们如何实现?
|
||||
|
||||
![](brick.jpg)
|
||||
|
||||
第一步我们需要知道某行的线程是奇数还是偶数,以为我们可以通过奇偶来决定是否要在x方向上偏移那一行。
|
||||
|
||||
____我们需要两段来解决这个问题____
|
||||
|
||||
要判断我们的线程是一个奇数行或者偶数行,我们要用 ```2.0``` 的 [```mod()```](../glossary/?search=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>
|
||||
|
||||
正如你所见,我们可以用一个 [三元算符号](https://en.wikipedia.org/wiki/%3F:) (第二行)来检查 ```2.0``` 的[```mod()```](../glossary/?search=mod)(余数)小于 ```1.0``` 或者类似地,我们用 [```step()```](../glossary/?search=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.html#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
|
||||
|
||||
## Truchet 瓷砖
|
||||
|
||||
目前我们学了如何区分奇数行/列或偶数行/列,(类似的),(我们也)可能再用(这个技巧)根据位置来设计元素。 考虑到 [Truchet Tiles](http://en.wikipedia.org/wiki/Truchet_tiles) 的例子,即一个单一设计元素可以以四种不同的方式呈现:
|
||||
|
||||
![](truchet-00.png)
|
||||
|
||||
通过改变对角瓷砖的图案,便可能组成无限种复杂设计的可能。
|
||||
|
||||
![](truchet-01.png)
|
||||
|
||||
仔细观察 ```rotateTilePattern()``` 函数, 它把坐标空间细分成四个单元并赋予每一个旋转值。
|
||||
|
||||
<div class="codeAndCanvas" data="truchet.frag"></div>
|
||||
|
||||
* 注释,取消注释,以及复制第69到72行来创作新的设计。
|
||||
|
||||
* 把黑白三角变成其他元素,例如:半圆,旋转的方形或直线。
|
||||
|
||||
* 编写根据元素自身位置旋转的图形代码。
|
||||
|
||||
* 创作一个根据其位置改变其他属性的图案。
|
||||
|
||||
* 想想其他能用这章原理的案例,不一定是图案. (例: 易经卦)
|
||||
|
||||
<a href="../edit.html#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
|
||||
|
||||
## 制定自己的规则
|
||||
|
||||
制作程序图案是种寻找最小可重复元素的古老练习(灵修)。我们作为长时间使用网格和图案来装饰织物、地面和物品的镶边物种:从古希腊的弯曲图案,到中国的窗栅设计,重复和变化的愉悦吸引我们的想象。花些时间浏览 [decorative](https://archive.org/stream/traditionalmetho00chririch#page/130/mode/2up) [patterns](https://www.pinterest.com/patriciogonzv/paterns/) 并看看在漫长的历史里,艺术家和设计师是如何寻找在秩序的预测性和(由)混沌和衍变(产生)的惊奇之间的边界的。从阿拉伯几何图案,到斑斓的非洲编制艺术,这里有一整个图案的宇宙要学习。
|
||||
|
||||
![Franz Sales Meyer - A handbook of ornament (1920)](geometricpatters.png)
|
||||
|
||||
这章我们就结束算法绘图部分了。在接下来的章节,我们会学习如何把熵(其实就是混乱和随机的意思)加入到我们的着色器中来产生生成设计。
|
0
09/TODO.md
Normal file
BIN
09/bricks.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
09/checks.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
09/diamondtiles.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
09/grid-making.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
09/iching-01.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
09/marching_dots.png
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
09/truchet.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
10/2d-random-mosaic.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
10/2d-random-truchet.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
10/2d-random.png
Normal file
After Width: | Height: | Size: 2.3 MiB |
93
10/README-ch.md
Executable file
@ -0,0 +1,93 @@
|
||||
# 生成设计
|
||||
|
||||
It is not a surprise that after so much repetition and order the author is forced to bring some chaos.
|
||||
|
||||
## 随机
|
||||
|
||||
[![Ryoji Ikeda - test pattern (2008) ](ryoji-ikeda.jpg) ](http://www.ryojiikeda.com/project/testpattern/#testpattern_live_set)
|
||||
|
||||
Randomness is a maximal expression of entropy. How can we generate randomness inside the seemingly predictable and rigid code environment?
|
||||
|
||||
让我们从分析下面的函数着手:
|
||||
|
||||
<div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div>
|
||||
|
||||
以上,我们提取sin函数其波形的分数部分。[```sin()```](../glossary/?search=sin) 函数值在 ```-1.0``` 到 ```1.0``` 之间浮点分布,返回作业在 ```0.0``` 到 ```1.0``` 间的正值。(这里翻译有些奇怪)我们可以用这种效果通过把 [```sin(x)```](../glossary/?search=sin) 打散成小片段来得到一些伪随机数。如何实现?乘以大些的sin值。在上面函数的相应位置加些0.
|
||||
|
||||
当你加到 ```100000.0``` (方程看起来是这样的:```y = fract(sin(x)*100000.0)``` ),你再也区分不出sin波了。小数部分的粒度将sine的循环变成了伪随机的混沌。
|
||||
|
||||
## 控制混沌
|
||||
|
||||
使用随机会很难;它不是太混沌难测就是有时又不够混乱。看看下面的图例。要实现这样的效果,我们像之前描述的那样应用用 ```rand()``` 函数。
|
||||
|
||||
细看,你可以看到 [```sin()```](../glossary/?search=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](https://pixelero.wordpress.com) 出版了 [interesting article about random distribution](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/)。 我添加了些前几张图所有的函数来供你试验,看看如何改变分布。取消函数的注释,看看发生什么变化。
|
||||
|
||||
如果你读下 [Pixelero's article](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/),一定谨记我们用的 ```rand()``` 是确定性随机,也被称作是伪随机。这就意味着, 就 ```rand(1.)``` 为例,总会返回相同的值。[Pixelero](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/) 用 ActionSript 函数做了些参考,```Math.random()```,一个非确定性随机;每次调用都返回不同的值。
|
||||
|
||||
## 2D 随机
|
||||
|
||||
现在我们对随机有了深入的理解,是时候将它应用到二维,```x``` 轴和 ```y``` 轴。为此我们需要将一个二维向量转化为一维浮点数。这里有几种不同的方法来实现,但 [```dot()```](../glossary/?search=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()```](../glossary/?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>
|
||||
|
||||
这里我用前一章的 ```truchetPattern()``` 函数根据单元产生的随机值来随机画一个方向的对角线。
|
||||
|
||||
你可以通过取消50到53行的代码块的注释得到其他有趣的图案,或者通过取消35和36行来得到图案的动画。
|
||||
|
||||
## 掌握随机
|
||||
|
||||
[Ryoji Ikeda](http://www.ryojiikeda.com/),日本电子作曲家、视觉艺术家,是运用随机的大师;他的作品是如此的富有感染力而难忘。他在音乐和视觉媒介中随机的运用,不再是混乱无序,反而以假乱真地折射出我们技术文化的复杂性。
|
||||
|
||||
<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](http://www.ryojiikeda.com/) 的作品并试试看下面的练习:
|
||||
|
||||
* 做按行随机移动的单元(以相反方向)。只显示亮一些的单元。让各行的速度随时间变化。
|
||||
|
||||
<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>
|
||||
|
||||
* 同样地,让某几行以不同的速度和方向。用鼠标位置关联显示单元的阀值。
|
||||
|
||||
<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>
|
||||
|
||||
* 创早其他有趣的效果。
|
||||
|
||||
<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>
|
||||
|
||||
优雅的使用随机是困难的,尤其是你希望创作自然的模拟。随机仅仅是过于混乱了,真实生活中很少有东西看上去如此 ```random()```。如果观察(玻璃床上)雨滴的肌理或是股票的曲线 — 这两个都挺随机的 — 但是他们和我们在章开始的随机图案看起来不是同一对爹妈生的。原因?嗯,随机值是没有因果关系的,而大多数自然图案(肌理)都对前一个状态有所记忆(基于前一个状态)。
|
||||
|
||||
下一章我们将学习噪波,一种光滑 和 *自然的* 创作计算机混沌的方式。
|
0
10/TODO.md
Normal file
BIN
10/ikeda-00.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
10/ikeda-03.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
10/ikeda-04.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
11/2d-noise.png
Normal file
After Width: | Height: | Size: 279 KiB |
220
11/README-ch.md
Normal file
@ -0,0 +1,220 @@
|
||||
|
||||
![NASA / WMAP science team](mcb.jpg)
|
||||
|
||||
## Noise 噪声
|
||||
|
||||
稍事休息,我们来转换一下思路。我们已经玩过了看起来像电视白噪音的 random 函数,尽管脑子里还在嗡嗡地转着 shader,但是已经眼花缭乱了。是时候出去走走了。
|
||||
|
||||
我们感知得到浮动在皮肤上的空气,晒在脸上的阳光。世界如此生动而丰富。颜色,质地,声音。当我们走在路上,不免会注意到路面、石头、树木和云朵的表面的样子。
|
||||
|
||||
![](texture-00.jpg)
|
||||
![](texture-01.jpg)
|
||||
![](texture-02.jpg)
|
||||
![](texture-03.jpg)
|
||||
![](texture-04.jpg)
|
||||
![](texture-05.jpg)
|
||||
![](texture-06.jpg)
|
||||
|
||||
这些纹理的不可预测性可以叫做“random"(随机),但是它们看起来不像是我们之前玩的 random。「真实世界」是如此的丰富而复杂!我们如何才能才能用计算机模拟这些多样的纹理呢?
|
||||
|
||||
这就是 [Ken Perlin](https://mrl.nyu.edu/~perlin/) 想要解答的问题。在20世纪80年代早期,他被委任为电影 “Tron”(电子世界争霸战)制作现实中的纹理。为了解决这个问题,他想出了一个优雅的算法,且获得了**奥斯卡奖**(名副其实)。
|
||||
|
||||
![Disney - Tron (1982)](tron.jpg)
|
||||
|
||||
下面这个并不是经典的 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()```](.../glossary/?search=floor) 获取 ```i```,用 [```fract()```](.../glossary/?search=fract) 获取 ```f```。然后我们 ```rand()``` x 的整数部分,即根据这个整数生成一个随机值。
|
||||
|
||||
在这之后有两个被注释掉的语句。第一句的作用是线性插值。
|
||||
|
||||
```glsl
|
||||
y = mix(rand(i), rand(i + 1.0), f);
|
||||
```
|
||||
|
||||
试试取消这句的注释,看一下会变成什么样子。注意我们储存在 `f` 中的 [```fract()```](.../glossary/?search=fract) 值[```mix()```](.../glossary/?search=mix)了两个随机值。
|
||||
|
||||
到本书的这个部分,我们知道我们可以比比线性插值做得更好,不是吗?现在试试取消第二句的注释。第二句用 [```smoothstep()```](.../glossary/?search=smoothstep)进行插值。
|
||||
|
||||
```glsl
|
||||
y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
|
||||
```
|
||||
|
||||
在取消注释后,注意顶点的变化如何变得顺滑了起来。在一些 noise 的应用中你会发现程序员喜欢用他们自己的**三次多项式函数**(比如下面的例子),而不是用 [```smoothstep()```](.../glossary/?search=smoothstep)。
|
||||
|
||||
```glsl
|
||||
float 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 算法被无数次用到各种语言和各种维度的设计中,制作出无数迷人的作品。
|
||||
|
||||
![Robert Hodgin - Written Images (2010)](robert_hodgin.jpg)
|
||||
|
||||
现在轮到你了:
|
||||
|
||||
* 写你自己的 ```float noise(float x)``` 函数。
|
||||
|
||||
* 用你的 noise 函数为图形制作动效,可以移动,旋转或改变大小。
|
||||
|
||||
* 用 noise 让一群图形一起“跳舞”。
|
||||
|
||||
* 用 noise 制做有机的形态。
|
||||
|
||||
* 创作一个“生物”,给它添加更多独特的动作,使其成为独特的角色。
|
||||
|
||||
## 2D Noise
|
||||
|
||||
![](02.png)
|
||||
|
||||
现在我们知道了如何在一维使用 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.)```)中插值。
|
||||
|
||||
![](01.png)
|
||||
|
||||
同样,如果我们想要在三维中使用 noise,就需要在一个立方体的8个角中插值。这个技术的重点在于插入随机值,所以我们叫它 **value noise**。
|
||||
|
||||
![](04.jpg)
|
||||
|
||||
就像一维的那个例子,这个插值不是线性的,而是三次方的,它会平滑地在方形网格中插入点。
|
||||
|
||||
![](05.jpg)
|
||||
|
||||
让我们来看一下这个方程。
|
||||
|
||||
<div class="codeAndCanvas" data="2d-noise.frag"></div>
|
||||
|
||||
我们先把空间大小变成五倍(第 45 行)以便看清栅格间的插值。然后在 noise 函数中我们把空间分成更小的单元。我们把它的整数部分和非整数部分都储存在这个单元里。我们计算整数位置的顶点的坐标,并给每个顶点生成一个随机值(第 23 - 26 行)。最后,在第 35 行用我们之前储存的小数位置的值,在四个顶点的随机值之间插值。
|
||||
|
||||
现在又到你了。试试下面的练习:
|
||||
|
||||
* 改变第 45 行的乘数。添加动态试试。
|
||||
|
||||
* 缩放到什么程度会让 noise 再度变得像 random?
|
||||
|
||||
* 缩放到什么程度就看不出来 noise 了?
|
||||
|
||||
* 试试看把 noise 函数和鼠标位置连起来。
|
||||
|
||||
* 如果我们把 noise 的斜率处理成距离场(distance field)会怎么样?用它来做点有趣的事情。
|
||||
|
||||
* 现在你已经可以在混沌和有序之间进行一些操控了,是时候把这些知识用起来了。用长方形,色彩,和 noise 做一幅 [Mark Rothko](http://en.wikipedia.org/wiki/Mark_Rothko) 的复杂的画作吧。
|
||||
|
||||
![Mark Rothko - Three (1950)](rothko.jpg)
|
||||
|
||||
## 生成式设计中的 noise 应用
|
||||
|
||||
Noise 算法的设计初衷是将难以言说的自然质感转化成数字图像。在目前我们看到的一维和二维的实践中,都是在random *values*(随机值)之间插值,所以它们才被叫做 **Value Noise**,但是还有很多很多获取 noise 的方法……
|
||||
|
||||
[ ![Inigo Quilez - Value Noise](value-noise.png) ](../edit.html#11/2d-vnoise.frag)
|
||||
|
||||
如你所见,value noise 看起来非常“块状”。为了消除这种块状的效果,在 1985 年 [Ken Perlin](https://mrl.nyu.edu/~perlin/) 开发了另一种 noise 算法 **Gradient Noise**。Ken 解决了如何插入随机的 *gradients*(梯度、渐变)而不是一个固定值。这些梯度值来自于一个二维的随机函数,返回一个方向(```vec2``` 格式的向量),而不仅是一个值(```float```格式)。点击下面的图片查看代码,看这个函数是如何运作的。
|
||||
|
||||
[ ![Inigo Quilez - Gradient Noise](gradient-noise.png) ](../edit.html#11/2d-gnoise.frag)
|
||||
|
||||
花一分钟来看看 [Inigo Quilez](http://www.iquilezles.org/) 做的两个例子,注意 [value noise](https://www.shadertoy.com/view/lsf3WH) 和 [gradient noise](https://www.shadertoy.com/view/XdXGW8)的区别。
|
||||
|
||||
就像一个画家非常了解画上的颜料是如何晕染的,我们越了解 noise 是如何运作的,越能更好地使用 noise。比如,如果我们要用一个二维的 noise 来旋转空间中的直线,我们就可以制作下图的旋涡状效果,看起来就像木头表皮一样。同样地,你可以点击图片查看代码。
|
||||
|
||||
[ ![Wood texture](wood.png) ](../edit.html#11/wood.frag)
|
||||
|
||||
```glsl
|
||||
pos = rotate2d( noise(pos) ) * pos; // 旋转空间
|
||||
pattern = lines(pos,.5); // 画直线
|
||||
```
|
||||
|
||||
另一种用 noise 制作有趣的图案的方式是用 distance field(距离场)处理它,用用 [第七章](../07/)提到的招数。
|
||||
|
||||
[ ![Splatter texture](splatter.png) ](../edit.html#11/splatter.frag)
|
||||
|
||||
```glsl
|
||||
color += smoothstep(.15,.2,noise(st*10.)); // 黑色的泼溅点
|
||||
color -= smoothstep(.35,.4,noise(st*10.)); // 泼溅点上的洞
|
||||
```
|
||||
|
||||
第三种方法是用 noise 函数来变换一个形状。这个也需要我们在[第七章](../07/)学到的技术。
|
||||
|
||||
<a href="../edit.html#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
|
||||
|
||||
给你的练习:
|
||||
|
||||
* 你还能做出什么其他图案呢?花岗岩?大理石?岩浆?水?找三种你感兴趣的材质,用 noise 加一些算法把它们做出来。
|
||||
|
||||
* 用 noise 给一个形状变形。
|
||||
|
||||
* 把 noise 加到动作中会如何?回顾[第八章](../08/)。用移动 “+” 四处跑的那个例子,加一些 random 和 noise 进去。
|
||||
|
||||
* 用代码生成波洛克(Jackson Pollock)的画。
|
||||
|
||||
![Jackson Pollock - Number 14 gray (1948)](pollock.jpg)
|
||||
|
||||
## Simplex Noise
|
||||
|
||||
对于 Ken Perlin 来说他的算法所取得的成功是远远不够的。他觉得可以更好。在 2001 年的 Siggraph(译者注:Siggraph是由美国计算机协会「计算机图形专业组」组织的计算机图形学顶级年度会议)上,他展示了 “simplex noise”,simplex noise 比之前的算法有如下优化:
|
||||
|
||||
* 它有着更低的计算复杂度和更少乘法计算。
|
||||
* 它可以用更少的计算量达到更高的维度。
|
||||
* 制造出的 noise 没有明显的人工痕迹。
|
||||
* 有着定义得很精巧的连续的 gradients(梯度),可以大大降低计算成本。
|
||||
* 特别易于硬件实现。
|
||||
|
||||
我知道你一定在想:“这人是谁?”是的,他的工作非常杰出!但是说真的,他是如何优化算法的呢?我们已经知道在二维中他是如何在四个点(正方形的四个角)之间插值的;所以没错你已经猜到了,对于三维[(这里有个示例)](../edit.html#11/3d-noise.frag)和四维我们需要插入 8 个和 16 个点。对吧?也就是说对于 N 维你需要插入 2 的 n 次方个点(2^N)。但是 Ken 很聪明地意识到尽管很显然填充屏幕的形状应该是方形,在二维中最简单的形状却是等边三角形。所以他把正方形网格(我们才刚学了怎么用)替换成了单纯形等边三角形的网格。
|
||||
|
||||
![](simplex-grid-00.png)
|
||||
|
||||
这时 N 维的形状就只需要 N + 1 个点了。也就是说在二维中少了 1 个点,三维中少了 4 个,四维中则少了 11 个!巨大的提升!
|
||||
|
||||
在二维中插值过程和常规的 noise 差不多,通过在一组点之间插值。但是在这种情况下,改用单纯形网格,我们只需要给总共 3 个点插值。
|
||||
|
||||
![](simplex-grid-01.png)
|
||||
|
||||
这个单纯形网格是如何制作的?这是另一个聪明绝顶而十分优雅的做法。可以先把常规的四角网格分成两个等腰三角形,然后再把三角形歪斜成等边三角形。
|
||||
|
||||
![](simplex-grid-02.png)
|
||||
|
||||
然后,就像 [Stefan Gustavson 在这篇文献中说的](http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf): **“……通过观察转换后坐标的整数部分,我们就可以快速地判断哪个包含着两个单纯形的单元含有我们所需的点。并且通过比较 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()```](.../glossary/?search=smoothstep) 一样)替换成了四次 Hermite 函数( _f(x) = 6x^5-15x^4+10x^3_ )。这就使得函数曲线两端更“平”,所以每个格的边缘更加优雅地与另一个衔接。也就是说格子的过渡更加连续。你可以取消下面例子的第二个公式的注释,亲眼看看其中的变化(或者看[这个例子](https://www.desmos.com/calculator/2xvlk5xp8b))。
|
||||
|
||||
<div class="simpleFunction" data="
|
||||
// 三次 Hermite 曲线。和 SmoothStep() 一样
|
||||
y = x*x*(3.0-2.0*x);
|
||||
// 四次 Hermite 曲线
|
||||
//y = x*x*x*(x*(x*6.-15.)+10.);
|
||||
"></div>
|
||||
|
||||
注意曲线的末端发生了怎样的变化。你可以阅读 [Ken 自己的解释](http://mrl.nyu.edu/~perlin/paper445.pdf)了解更多。
|
||||
|
||||
所有这些进展汇聚成了算法中的杰作 **Simplex Noise**。下面是这个算法在 GLSL 中的应用,作者是 Ian McEwan,以[这篇论文](http://webstaff.itn.liu.se/~stegu/jgt2012/article.pdf)发表,对于我们的教学而言太复杂了,但你可以点开看看,也许没有你想象得那么晦涩难懂。
|
||||
|
||||
[ ![Ian McEwan of Ashima Arts - Simplex Noise](simplex-noise.png) ](../edit.html#11/2d-snoise-clear.frag)
|
||||
|
||||
好了,技术细节就说到这里,现在你可以利用它好好自由发挥一下:
|
||||
|
||||
* 凝神思考每个 noise 的实例的模样。设想它们是你的原材料,就像雕塑家手中的大理石。你觉得每一个带给你怎样不同的“感觉“?眯起你的眼睛释放想象力,就像你在观察云朵的形状的时候那样。你看到了什么?你想起了什么?你觉得每个 noise 生成的图像可以用来做成什么?放开胆量去做吧,用代码实现它。
|
||||
|
||||
* 做一个 shader 来表现流体的质感。比如像[熔岩灯](https://en.wikipedia.org/wiki/Lava_lamp?oldformat=true),墨水滴,水,等等。
|
||||
|
||||
<a href="../edit.html#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.html#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>
|
0
11/TODO.md
Normal file
BIN
11/circleWave-noise.png
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
11/iching-03.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
11/lava-lamp.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
11/simplex-grid.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
12/2d-fbm.png
Normal file
After Width: | Height: | Size: 280 KiB |
@ -1,2 +1,4 @@
|
||||
|
||||
https://docs.google.com/spreadsheets/d/194IVZR_xLVsw5H0zJZ7CWuimOQA_n8KU2eeIZBDwIH0/edit#gid=0
|
||||
|
||||
http://heman.readthedocs.org/en/latest/generate.html#archipelagos
|
||||
|
0
12/TODO.md
Normal file
BIN
12/clouds.png
Normal file
After Width: | Height: | Size: 812 KiB |
14
14/README.md
@ -16,7 +16,7 @@ vec4 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-imgs="hokusai.jpg"></div>
|
||||
<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 coordenates are consisten with the rest of the things we had saw and their coordenates are between 0.0 and 1.0 whitch match perfectly with the normalized space coordinates we have been using.
|
||||
|
||||
@ -34,7 +34,7 @@ Finnally, you can setup your image to repeat in the edges, so if you give values
|
||||
|
||||
All this features makes your images more like an infinit spandex fabric. You can streach and shrinks your texture without noticing the grid of bites they originally where compose of or the ends of it. To experience this take a look to the following code where we distort a texture using [the noise function we already made](../11/).
|
||||
|
||||
<div class="codeAndCanvas" data="texture-noise.frag" data-imgs="hokusai.jpg"></div>
|
||||
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>
|
||||
|
||||
## Texture resolution
|
||||
|
||||
@ -46,7 +46,7 @@ How we can solve this problem? Well we need to know the original proportions of
|
||||
|
||||
Uncomment line 21 of the following code to see this in action.
|
||||
|
||||
<div class="codeAndCanvas" data="texture-resolution.frag" data-imgs="nicephore.jpg"></div>
|
||||
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>
|
||||
|
||||
* What we need to do to center this image?
|
||||
|
||||
@ -62,11 +62,17 @@ This level of craftsmanship links back to some of the first optical experiments
|
||||
|
||||
This could seam simple but the posibilities of modifing textures coordinates is enormus. For example: .
|
||||
|
||||
<div class="codeAndCanvas" data="texture-sprite.frag" data-imgs="muybridge.jpg"></div>
|
||||
<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 learn?
|
||||
|
||||
* Way before Oculus or google cardboard, stereoscopic photography was a big thing. Could code a simple shader to re-use this beautiful images?
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
* 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 finnaly the complexity of shader makes sense, because was in a big sense designed to do this type of process. We will start doing some image operations!
|
0
14/TODO.md
Normal file
BIN
14/texture-noise.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
14/texture-resolution.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
14/texture-sprite.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
14/texture-stereo-00.jpg
Normal file
After Width: | Height: | Size: 416 KiB |
BIN
14/texture-stereo-01.jpg
Normal file
After Width: | Height: | Size: 352 KiB |
BIN
14/texture-stereo-02.jpg
Normal file
After Width: | Height: | Size: 866 KiB |
BIN
14/texture-stereo-03.jpg
Normal file
After Width: | Height: | Size: 1018 KiB |
BIN
14/texture-stereo-04.jpg
Normal file
After Width: | Height: | Size: 126 KiB |
BIN
14/texture-stereo-05.jpg
Normal file
After Width: | Height: | Size: 2.6 MiB |
BIN
14/texture-stereo-06.jpg
Normal file
After Width: | Height: | Size: 587 KiB |
31
14/texture-stereo.frag
Normal file
@ -0,0 +1,31 @@
|
||||
// Author @patriciogv - 2015
|
||||
// http://patriciogonzalezvivo.com
|
||||
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_tex0Resolution;
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform vec2 u_mouse;
|
||||
uniform float u_time;
|
||||
|
||||
void main () {
|
||||
vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
vec4 color = vec4(vec3(0.0),1.);
|
||||
|
||||
st *= vec2(.5,1.);
|
||||
|
||||
// st.x *= u_resolution.x/u_resolution.y;
|
||||
float t = u_time*24.;
|
||||
float pct = step(.75,abs(sin((st.x+t)*3.1415*10.)));
|
||||
|
||||
vec4 A = texture2D(u_tex0,st);
|
||||
vec4 B = texture2D(u_tex0,st+vec2(.5,.0));
|
||||
|
||||
color = mix(A, B, abs(sin(t)));
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
BIN
14/texture.png
Normal file
After Width: | Height: | Size: 40 KiB |
0
15/TODO.md
Normal file
BIN
15/blend.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
15/inv.png
Normal file
After Width: | Height: | Size: 212 KiB |
BIN
15/operations.png
Normal file
After Width: | Height: | Size: 65 KiB |