Введение
Недавно я изучил основы WebGL, чтобы немного больше поиграть с фракталами.
- Треугольник Серпинского и игра хаоса
- Паскаль и треугольник Серпинского
- Еще треугольники Серпинского внутри треугольника Паскаля? 🤯
- Визуализация уравнения Мандельброта
- Мандельброт в SVG
- Новое поколение Мандельброта?
Что я очень быстро обнаружил, так это то, что WebGL ужасен. Прежде чем я мог начать что-либо делать, мне пришлось так много печатать:
- Очистите буферы
color
иdepth
. - Создайте объекты вершин и фрагментов
WebGLShader
. - Скомпилируйте указанные шейдеры.
- Проверьте статус компиляции.
- Получить любые ошибки компиляции.
- Создайте файл
WebGLProgram
. - Прикрепите
WebGLShader
объектов. - Связать
WebGLProgram
. - Проверьте статус привязки.
- Получите любые ошибки связывания.
- Вероятно, проверьте файл
WebGLProgram
. - Получите любые ошибки проверки.
Я нашел это очень, очень утомительным, поэтому я сделал то, что делают инженеры-программисты, и сделал это лучше.
Код
Он принимает контекст WebGL (т. е. то, что возвращается функцией HTMLCanvasElement.getContext()
) вместе с текстом для двух шейдеров и возвращает WebGLProgram
, готовый для реальных действий. Если что-то выйдет из строя, будет выдано исключение с подробной информацией о сбое.
/** * Accept the shaders and WebGL context and perform the steps required to create a <code>WebGLProgram</code> * * @param webGlContext {WebGLRenderingContext | WebGL2RenderingContext} * @param vertexShaderText {string} * @param fragmentShaderText {string} * @param verify {boolean} * @throws Error containing further details of error. * @returns {WebGLProgram} Compiled WebGL program. */ export function createProgram( webGlContext, vertexShaderText, fragmentShaderText, verify = true ) { if (!webGlContext) { console.error("This browser doesn't support WebGL"); } webGlContext.clearColor(0.0, 0.0, 0.0, 0.0); webGlContext.clear( webGlContext.COLOR_BUFFER_BIT | webGlContext.DEPTH_BUFFER_BIT ); const vertexShader = webGlContext.createShader(webGlContext.VERTEX_SHADER); const fragmentShader = webGlContext.createShader( webGlContext.FRAGMENT_SHADER ); webGlContext.shaderSource(vertexShader, vertexShaderText); webGlContext.shaderSource(fragmentShader, fragmentShaderText); webGlContext.compileShader(vertexShader); webGlContext.compileShader(fragmentShader); const compileStatus = { vertexStatus: webGlContext.getShaderParameter( vertexShader, webGlContext.COMPILE_STATUS ) || webGlContext.getShaderInfoLog(vertexShader), fragmentStatus: webGlContext.getShaderParameter( fragmentShader, webGlContext.COMPILE_STATUS ) || webGlContext.getShaderInfoLog(fragmentShader), }; if ( compileStatus.vertexStatus !== true || compileStatus.fragmentStatus !== true ) { throw new Error( `Failed to compile. ${JSON.stringify(compileStatus, null, 2)}` ); } const program = webGlContext.createProgram(); webGlContext.attachShader(program, vertexShader); webGlContext.attachShader(program, fragmentShader); webGlContext.linkProgram(program); const linkingStatus = webGlContext.getProgramParameter(program, webGlContext.LINK_STATUS) || webGlContext.getProgramInfoLog(program); if (linkingStatus !== true) { throw new Error(`Linking filed:\n${linkingStatus}`); } if (verify) { webGlContext.validateProgram(program); const validationStatus = webGlContext.getProgramParameter( program, webGlContext.VALIDATE_STATUS ) || webGlContext.getProgramInfoLog(program); if (validationStatus !== true) { throw new Error(`Validation failed.\n${validationStatus}`); } } return program; }
Пример использования
const CANVAS = document.getElementById('my-canvas'); const gl = CANVAS.getContext('webgl'); const vertexShaderText = '...'; const fragmentShaderText = '...'; const program = createProgram(gl, vertexShaderText, fragmentShaderText);
Заключение
Я намного счастливее, теперь мне не нужно больше на это смотреть. Я надеюсь, что это полезно и для вас.
Спасибо за прочтение.