Введение

Недавно я изучил основы WebGL, чтобы немного больше поиграть с фракталами.

Что я очень быстро обнаружил, так это то, что WebGL ужасен. Прежде чем я мог начать что-либо делать, мне пришлось так много печатать:

  1. Очистите буферы colorи depth.
  2. Создайте объекты вершин и фрагментов WebGLShader.
  3. Скомпилируйте указанные шейдеры.
  4. Проверьте статус компиляции.
  5. Получить любые ошибки компиляции.
  6. Создайте файл WebGLProgram.
  7. Прикрепите WebGLShader объектов.
  8. Связать WebGLProgram.
  9. Проверьте статус привязки.
  10. Получите любые ошибки связывания.
  11. Вероятно, проверьте файл WebGLProgram.
  12. Получите любые ошибки проверки.

Я нашел это очень, очень утомительным, поэтому я сделал то, что делают инженеры-программисты, и сделал это лучше.

Код

Он принимает контекст 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);

Заключение

Я намного счастливее, теперь мне не нужно больше на это смотреть. Я надеюсь, что это полезно и для вас.

Спасибо за прочтение.