Hàm tạo hình

Starter pack

Logic của shader rất đặc biệt, nó khác so với những gì chúng ta quen thuộc trong JavaScript hoặc các ngôn ngữ lập trình khác. Mặc dù cú pháp khá giống nhau, nhưng cách ta triển khai logic thì khác.

Nó giống một hộp đen, bạn đưa vào một số dữ liệu và nhận được một số màu sắc cho fragment shader, hoặc một số vị trí cho vertex shader.

Nhưng làm thế nào để bạn kiểm soát đầu ra? Làm thế nào để bạn làm cho nó trông như bạn muốn? Đây là lúc các hàm tạo hình ra đời.

Chúng ta sẽ tập trung vào fragment shader cho bài học này, nhưng các nguyên tắc tương tự cũng áp dụng cho vertex shader.

Khu vực thực hành

Để thành thạo nghệ thuật tạo hình, bạn cần phải thực hành và thử nghiệm. Sẽ mất thời gian để quen với cách hoạt động của shader, nhưng khi bạn nắm vững, bạn sẽ có thể tạo ra những hiệu ứng tuyệt vời.

Vì tôi muốn bạn có được vị trí tốt nhất để thành công, tôi đã chuẩn bị cảnh Phòng Trưng Bày Nghệ Thuật 3D này để bạn có thể nhìn thấy shader của mình trong thời gian thực trong một môi trường dễ chịu.

Bạn có cảm nhận được sự sáng tạo đang tuôn trào không? Tất cả những khung trống đó đang chờ đợi kiệt tác của bạn!

Mô hình 3D được sử dụng là VR Gallery của Maxim Mavrichev và được cấp phép theo Creative Commons Attribution.

Tôi đã sử dụng <CameraControls /> để tạo ra camera góc nhìn thứ nhất để điều hướng bên trong phòng trưng bày và Squoosh để giảm kích thước của các texture.

SimpleShaderMaterial

Tất cả các khung từ phòng trưng bày đều là bản sao của SimpleShaderMaterial, đó là một shader tùy chỉnh cơ bản mà tôi đã chuẩn bị cho bạn với:

  • uColor: một màu sắc
  • uTime: thời gian đã trôi qua kể từ khi bắt đầu ứng dụng
  • vUv: tọa độ UV của đoạn (từ 0 đến 1 trên cả hai trục)

Tất cả đều được mở rộng trong <App.jsx /> để có thể sử dụng chúng theo cách khai báo trong cảnh của chúng ta.

Chúng được đặt tên dựa trên vị trí của chúng trong phòng trưng bày, nhưng hãy thoải mái đổi tên chúng thành một cái gì đó có ý nghĩa hơn một khi bạn đã tạo ra những kiệt tác với chúng!

Hàm

Đối với mỗi đoạn nhỏ của shader của chúng ta, cùng một đoạn mã sẽ được thực thi với các đầu vào khác nhau.

Nếu chúng ta muốn vẽ một đường thẳng, chúng ta có thể sử dụng các câu lệnh if để kiểm tra xem pixel có nằm trong đường thẳng hay không. Nhưng việc làm các hình dạng phức tạp hơn sẽ rất khó và kém hiệu quả.

Thay vào đó, chúng ta sử dụng hàm để định hình đầu ra của chúng ta. Đừng lo lắng, bạn không cần phải là một chuyên gia toán học để sử dụng chúng. Bạn chỉ cần hiểu các hàm nào có sẵn và chúng thực hiện những gì.

Hãy nghĩ về một hàm như một cỗ máy nhận một số đầu vào và cung cấp cho bạn một số đầu ra. Ví dụ, hàm f(x) = x * 2 nhận một số x và cho bạn kết quả là x * 2.

Để hình dung hiệu quả của các hàm khác nhau, chúng ta sử dụng Graphtoy, đây là công cụ đơn giản để nhập các hàm và xem đầu ra của chúng. Nó giúp xác nhận sự hiểu biết của chúng ta về hàm khi thử nghiệm trong shader.

Hãy hình dung hàm f(x) = x * 2 của chúng ta trong Graphtoy:

Graphtoy

Graphtoy sẽ nhanh chóng trở thành người bạn tốt nhất của bạn khi bạn thử nghiệm với shader.

Đã đến lúc thử nghiệm với các hàm khác nhau mà chúng ta có.

Hàm Step

Hàm step là một hàm đơn giản trả về 0 nếu đầu vào nhỏ hơn một ngưỡng và 1 nếu đầu vào lớn hơn ngưỡng.

Nó nhận hai tham số:

  • edge: ngưỡng
  • x: giá trị đầu vào

Hãy thử nó trên đoạn shader ArtFront02Material.jsx:

void main() {
  float pct = 1.0;
  pct = step(0.5, vUv.x);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Chúng ta khai báo một biến pct để xác định tỷ lệ phần trăm của màu sắc mà chúng ta muốn hiển thị. Chúng ta thiết lập nó mặc định là 1.0, sau đó sử dụng hàm step để đặt pct thành 0.0 nếu vUv.x nhỏ hơn 0.5.

Biểu diễn hàm Step

Chúng ta có thể thấy nửa bên trái của frame là màu đen, và nửa bên phải là màu được đặt trong uColor uniform.

Hãy áp dụng nó cho trục dọc với một ngưỡng khác:

void main() {
  float pct = 1.0;
  pct = step(0.2, vUv.y);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Biểu diễn hàm Step dọc

Chúng ta có thể thấy 20% phía dưới của frame là màu đen, và phần còn lại là màu tím.

Tọa độ UV có nguồn gốc từ góc dưới bên trái của frame, vì vậy [0, 0] là góc dưới bên trái, và [1, 1] là góc trên bên phải.

Nếu bạn muốn đảo ngược hiệu ứng, bạn có thể đơn giản hoán đổi các tham số của hàm step:

void main() {
  float pct = 1.0;
  pct = step(vUv.y, 0.2);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Biểu diễn hàm Step dọc đảo ngược

Chúng ta bây giờ đã có hiệu ứng ngược lại.

Mix

Hàm mix function là một hàm đơn giản trả về một nội suy tuyến tính giữa hai giá trị.

Nó nhận ba tham số:

  • x: giá trị thứ nhất
  • y: giá trị thứ hai
  • a: giá trị để nội suy giữa xy

Hãy thử nó:

void main() {
  float pct = 1.0;
  pct = mix(0.0, 1.0, vUv.x);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Mix function representation

Chúng ta có thể thấy một gradient từ đen đến tím.

Giống như nhiều hàm khác, bạn có thể sử dụng nó trên các loại dữ liệu khác, chẳng hạn như các thành phần vector. Hãy sử dụng nó để tạo một gradient từ trắng đến tím trên trục dọc:

void main() {
  vec3 whiteColor = vec3(1.0);
  vec3 finalColor = mix(whiteColor, uColor, vUv.y);
  gl_FragColor = vec4(finalColor, 1.0);
}

Mix function two colors

Chúng ta có thể thấy một gradient từ trắng đến tím.

Hãy thay đổi giá trị nội suy bằng cách gọi hàm mix lần đầu để lấy giá trị pct, và sau đó sử dụng nó để nội suy giữa whiteColoruColor:

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = mix(0.0, 0.3, vUv.y);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Mix function two colors with pct

Màu tím rất nhạt vì giá trị lớn nhất của pct0.3.

Sử dụng Graphtoy để thử nghiệm với hàm mix và hiểu cách nó hoạt động.

Graphtoy mix function

3 nội suy khác nhau với hàm mix cho ra kết quả hoàn toàn khác nhau.

Smoothstep

Hàm smoothstep là một hàm đơn giản trả về một phép nội suy mượt mà giữa hai giá trị.

Nó nhận ba tham số:

  • edge0: ranh giới dưới
  • edge1: ranh giới trên
  • x: giá trị để nội suy giữa edge0edge1

Nó cho ba kết quả khác nhau:

  • 0.0 nếu x nhỏ hơn edge0
  • 1.0 nếu x lớn hơn edge1
  • một phép nội suy mượt mà giữa 0.01.0 nếu x nằm giữa edge0edge1

Hãy sử dụng nó để thay đổi giá trị pct của chúng ta:

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = smoothstep(0.4, 0.6, vUv.y);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Smoothstep function representation

Chuyển tiếp giờ chỉ diễn ra giữa 0.40.6. Mọi thứ bên dưới là màu trắng, và mọi thứ bên trên là màu tím.

Min & Max

minmax là những hàm đơn giản trả về giá trị nhỏ nhất hoặc lớn nhất giữa hai đầu vào. Chúng hoạt động giống hệt như các hàm Math.minMath.max trong JavaScript.

Hãy sử dụng chúng để tinh chỉnh giá trị pct của chúng ta. Trước tiên, hãy đảm bảo giá trị này không bao giờ dưới 0.4 (nghĩa là không bao giờ hoàn toàn trắng):

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = smoothstep(0.4, 0.6, vUv.y);
  pct = max(pct, 0.4);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Và không bao giờ vượt quá 0.6 (nghĩa là không bao giờ hoàn toàn tím):

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = smoothstep(0.4, 0.6, vUv.y);
  pct = max(pct, 0.4);
  pct = min(pct, 0.6);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Min & Max function representation

Màu sắc bị nhạt đi, và chuyển đổi rất mượt mà.

Hãy hình dung nó trên Graphtoy:

Max graph

Đại diện max(x, 0.4)

Min graph

Đại diện min(x, 0.6)

Và nếu chúng ta kết hợp chúng:

Min & Max graph

Bạn có thể thấy các giá trị của chúng ta không bao giờ xuống dưới 0.4 và không bao giờ vượt quá 0.6.

Sự kết hợp giữa các hàm minmax của chúng ta có thể được thay thế bằng hàm clamp, là cách viết tắt cho min(max(x, min), max).

Các phép toán & mẫu

Trước khi chúng ta khám phá nhiều hàm hữu dụng khác, hãy xem cách chúng ta có thể sử dụng các phép toán để tạo ra các mẫu.

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.