Введение
Недавно я изучил основы 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);
Заключение
Я намного счастливее, теперь мне не нужно больше на это смотреть. Я надеюсь, что это полезно и для вас.
Спасибо за прочтение.