-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* inital draft * Update dependencies * avoid repetitive json parsing * Move input class * Reduce complexity * Add json example * Fix json example * Add tests for fail cases * update ruff to 0.3.0 * Add online tests * Add documentation * fix typos * Address comments
- Loading branch information
Showing
13 changed files
with
444 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
{ | ||
"Shader": { | ||
"ver": "0.1", | ||
"info": { | ||
"id": "MllSzX", | ||
"date": "1438698189", | ||
"viewed": 14064, | ||
"name": "Bicubic Texture Filtering", | ||
"username": "demofox", | ||
"description": "Nearest neighbor texture filtering on left, Bilinear texture filtering in left middle, Lagrange Bicubic texture filtering on middle right, cubic hermite on the right. Use the mouse to control pan / zoom.\n", | ||
"likes": 61, | ||
"published": 3, | ||
"flags": 0, | ||
"usePreview": 0, | ||
"tags": [ | ||
"2d", | ||
"texturefilter" | ||
], | ||
"hasliked": 0 | ||
}, | ||
"renderpass": [ | ||
{ | ||
"inputs": [ | ||
{ | ||
"id": 16, | ||
"src": "/media/a/3083c722c0c738cad0f468383167a0d246f91af2bfa373e9c5c094fb8c8413e0.png", | ||
"ctype": "texture", | ||
"channel": 0, | ||
"sampler": { | ||
"filter": "mipmap", | ||
"wrap": "repeat", | ||
"vflip": "false", | ||
"srgb": "false", | ||
"internal": "byte" | ||
}, | ||
"published": 1 | ||
} | ||
], | ||
"outputs": [ | ||
{ | ||
"id": 37, | ||
"channel": 0 | ||
} | ||
], | ||
"code": "float c_textureSize = 64.0;\n\n#define c_onePixel (1.0 / c_textureSize)\n#define c_twoPixels (2.0 / c_textureSize)\n\nfloat c_x0 = -1.0;\nfloat c_x1 = 0.0;\nfloat c_x2 = 1.0;\nfloat c_x3 = 2.0;\n \n//=======================================================================================\nvec3 CubicLagrange (vec3 A, vec3 B, vec3 C, vec3 D, float t)\n{\n return\n A * \n (\n (t - c_x1) / (c_x0 - c_x1) * \n (t - c_x2) / (c_x0 - c_x2) *\n (t - c_x3) / (c_x0 - c_x3)\n ) +\n B * \n (\n (t - c_x0) / (c_x1 - c_x0) * \n (t - c_x2) / (c_x1 - c_x2) *\n (t - c_x3) / (c_x1 - c_x3)\n ) +\n C * \n (\n (t - c_x0) / (c_x2 - c_x0) * \n (t - c_x1) / (c_x2 - c_x1) *\n (t - c_x3) / (c_x2 - c_x3)\n ) + \n D * \n (\n (t - c_x0) / (c_x3 - c_x0) * \n (t - c_x1) / (c_x3 - c_x1) *\n (t - c_x2) / (c_x3 - c_x2)\n );\n}\n\n//=======================================================================================\nvec3 BicubicLagrangeTextureSample (vec2 P)\n{\n vec2 pixel = P * c_textureSize + 0.5;\n \n vec2 frac = fract(pixel);\n pixel = floor(pixel) / c_textureSize - vec2(c_onePixel/2.0);\n \n vec3 C00 = texture(iChannel0, pixel + vec2(-c_onePixel ,-c_onePixel)).rgb;\n vec3 C10 = texture(iChannel0, pixel + vec2( 0.0 ,-c_onePixel)).rgb;\n vec3 C20 = texture(iChannel0, pixel + vec2( c_onePixel ,-c_onePixel)).rgb;\n vec3 C30 = texture(iChannel0, pixel + vec2( c_twoPixels,-c_onePixel)).rgb;\n \n vec3 C01 = texture(iChannel0, pixel + vec2(-c_onePixel , 0.0)).rgb;\n vec3 C11 = texture(iChannel0, pixel + vec2( 0.0 , 0.0)).rgb;\n vec3 C21 = texture(iChannel0, pixel + vec2( c_onePixel , 0.0)).rgb;\n vec3 C31 = texture(iChannel0, pixel + vec2( c_twoPixels, 0.0)).rgb; \n \n vec3 C02 = texture(iChannel0, pixel + vec2(-c_onePixel , c_onePixel)).rgb;\n vec3 C12 = texture(iChannel0, pixel + vec2( 0.0 , c_onePixel)).rgb;\n vec3 C22 = texture(iChannel0, pixel + vec2( c_onePixel , c_onePixel)).rgb;\n vec3 C32 = texture(iChannel0, pixel + vec2( c_twoPixels, c_onePixel)).rgb; \n \n vec3 C03 = texture(iChannel0, pixel + vec2(-c_onePixel , c_twoPixels)).rgb;\n vec3 C13 = texture(iChannel0, pixel + vec2( 0.0 , c_twoPixels)).rgb;\n vec3 C23 = texture(iChannel0, pixel + vec2( c_onePixel , c_twoPixels)).rgb;\n vec3 C33 = texture(iChannel0, pixel + vec2( c_twoPixels, c_twoPixels)).rgb; \n \n vec3 CP0X = CubicLagrange(C00, C10, C20, C30, frac.x);\n vec3 CP1X = CubicLagrange(C01, C11, C21, C31, frac.x);\n vec3 CP2X = CubicLagrange(C02, C12, C22, C32, frac.x);\n vec3 CP3X = CubicLagrange(C03, C13, C23, C33, frac.x);\n \n return CubicLagrange(CP0X, CP1X, CP2X, CP3X, frac.y);\n}\n\n//=======================================================================================\nvec3 CubicHermite (vec3 A, vec3 B, vec3 C, vec3 D, float t)\n{\n\tfloat t2 = t*t;\n float t3 = t*t*t;\n vec3 a = -A/2.0 + (3.0*B)/2.0 - (3.0*C)/2.0 + D/2.0;\n vec3 b = A - (5.0*B)/2.0 + 2.0*C - D / 2.0;\n vec3 c = -A/2.0 + C/2.0;\n \tvec3 d = B;\n \n return a*t3 + b*t2 + c*t + d;\n}\n\n//=======================================================================================\nvec3 BicubicHermiteTextureSample (vec2 P)\n{\n vec2 pixel = P * c_textureSize + 0.5;\n \n vec2 frac = fract(pixel);\n pixel = floor(pixel) / c_textureSize - vec2(c_onePixel/2.0);\n \n vec3 C00 = texture(iChannel0, pixel + vec2(-c_onePixel ,-c_onePixel)).rgb;\n vec3 C10 = texture(iChannel0, pixel + vec2( 0.0 ,-c_onePixel)).rgb;\n vec3 C20 = texture(iChannel0, pixel + vec2( c_onePixel ,-c_onePixel)).rgb;\n vec3 C30 = texture(iChannel0, pixel + vec2( c_twoPixels,-c_onePixel)).rgb;\n \n vec3 C01 = texture(iChannel0, pixel + vec2(-c_onePixel , 0.0)).rgb;\n vec3 C11 = texture(iChannel0, pixel + vec2( 0.0 , 0.0)).rgb;\n vec3 C21 = texture(iChannel0, pixel + vec2( c_onePixel , 0.0)).rgb;\n vec3 C31 = texture(iChannel0, pixel + vec2( c_twoPixels, 0.0)).rgb; \n \n vec3 C02 = texture(iChannel0, pixel + vec2(-c_onePixel , c_onePixel)).rgb;\n vec3 C12 = texture(iChannel0, pixel + vec2( 0.0 , c_onePixel)).rgb;\n vec3 C22 = texture(iChannel0, pixel + vec2( c_onePixel , c_onePixel)).rgb;\n vec3 C32 = texture(iChannel0, pixel + vec2( c_twoPixels, c_onePixel)).rgb; \n \n vec3 C03 = texture(iChannel0, pixel + vec2(-c_onePixel , c_twoPixels)).rgb;\n vec3 C13 = texture(iChannel0, pixel + vec2( 0.0 , c_twoPixels)).rgb;\n vec3 C23 = texture(iChannel0, pixel + vec2( c_onePixel , c_twoPixels)).rgb;\n vec3 C33 = texture(iChannel0, pixel + vec2( c_twoPixels, c_twoPixels)).rgb; \n \n vec3 CP0X = CubicHermite(C00, C10, C20, C30, frac.x);\n vec3 CP1X = CubicHermite(C01, C11, C21, C31, frac.x);\n vec3 CP2X = CubicHermite(C02, C12, C22, C32, frac.x);\n vec3 CP3X = CubicHermite(C03, C13, C23, C33, frac.x);\n \n return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y);\n}\n\n//=======================================================================================\nvec3 BilinearTextureSample (vec2 P)\n{\n vec2 pixel = P * c_textureSize + 0.5;\n \n vec2 frac = fract(pixel);\n pixel = (floor(pixel) / c_textureSize) - vec2(c_onePixel/2.0);\n\n vec3 C11 = texture(iChannel0, pixel + vec2( 0.0 , 0.0)).rgb;\n vec3 C21 = texture(iChannel0, pixel + vec2( c_onePixel , 0.0)).rgb;\n vec3 C12 = texture(iChannel0, pixel + vec2( 0.0 , c_onePixel)).rgb;\n vec3 C22 = texture(iChannel0, pixel + vec2( c_onePixel , c_onePixel)).rgb;\n\n vec3 x1 = mix(C11, C21, frac.x);\n vec3 x2 = mix(C12, C22, frac.x);\n return mix(x1, x2, frac.y);\n}\n\n//=======================================================================================\nvec3 NearestTextureSample (vec2 P)\n{\n vec2 pixel = P * c_textureSize;\n \n vec2 frac = fract(pixel);\n pixel = (floor(pixel) / c_textureSize);\n return texture(iChannel0, pixel + vec2(c_onePixel/2.0)).rgb;\n}\n\n//=======================================================================================\nvoid AnimateUV (inout vec2 uv)\n{\n if (iMouse.z > 0.0)\n {\n uv -= vec2(0.0,0.5) * iResolution.y / iResolution.x;;\n uv *= vec2(iMouse.y / iResolution.y);\n uv += vec2(1.5 * iMouse.x / iResolution.x, 0.0);\n \n }\n else\n { \n \tuv += vec2(sin(iTime * 0.3)*0.5+0.5, sin(iTime * 0.7)*0.5+0.5);\n \tuv *= (sin(iTime * 0.3)*0.5+0.5)*3.0 + 0.2;\n }\n}\n\n//=======================================================================================\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord )\n{\n // set up our coordinate system\n float aspectRatio = iResolution.y / iResolution.x;\n vec2 uv = (fragCoord.xy / iResolution.xy);\n uv.y *= aspectRatio;\n \n // do our sampling\n vec3 color;\n if (abs(uv.x - (1.0/4.0)) < 0.0025)\n {\n color = vec3(1.0);\n } \n else if (abs(uv.x - (2.0/4.0)) < 0.0025)\n {\n color = vec3(1.0);\n } \n else if (abs(uv.x - (3.0/4.0)) < 0.0025)\n {\n color = vec3(1.0);\n } \n else if (uv.x < (1.0/4.0))\n {\n AnimateUV(uv);\n color = NearestTextureSample(uv);\n }\n else if (uv.x < (2.0/4.0))\n {\n uv -= vec2((1.0/4.0),0.0);\n AnimateUV(uv);\n color = texture(iChannel0, uv).xyz;\n //color = BilinearTextureSample(uv);\n }\n else if (uv.x < (3.0/4.0))\n {\n uv -= vec2((2.0/4.0),0.0);\n AnimateUV(uv);\n color = BicubicLagrangeTextureSample(uv);\n }\n else\n {\n uv -= vec2((3.0/4.0),0.0);\n AnimateUV(uv);\n color = BicubicHermiteTextureSample(uv);\n\t}\n \n // set the final color\n\tfragColor = vec4(color,1.0); \n}", | ||
"name": "Image", | ||
"description": "", | ||
"type": "image" | ||
} | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# run_example = false | ||
|
||
from wgpu_shadertoy import Shadertoy | ||
|
||
# shadertoy source: https://www.shadertoy.com/view/wtcSzN by tdhooper CC-BY-NC-SA-3.0 | ||
shader = Shadertoy.from_id("wtcSzN") | ||
|
||
if __name__ == "__main__": | ||
shader.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from pathlib import Path | ||
|
||
from wgpu_shadertoy import Shadertoy | ||
|
||
# shadertoy source: https://www.shadertoy.com/view/MllSzX by demofox CC-BY-NC-SA-3.0 | ||
json_path = Path(Path(__file__).parent, "shader_MllSzX.json") | ||
shader = Shadertoy.from_json(json_path) | ||
|
||
if __name__ == "__main__": | ||
shader.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,10 @@ build-backend = "setuptools.build_meta" | |
name = "wgpu-shadertoy" | ||
dynamic = ["version", "readme"] | ||
dependencies = [ | ||
"wgpu>=0.13.2,<0.14.0", | ||
"wgpu>=0.14.1,<0.15.0", | ||
"requests", | ||
"numpy", | ||
"Pillow", | ||
] | ||
description = "Shadertoy implementation based on wgpu-py" | ||
license = {file = "LICENSE"} | ||
|
@@ -18,6 +21,9 @@ authors = [ | |
{name = "Jan Kels", email = "[email protected]"}, | ||
] | ||
|
||
[project.scripts] | ||
wgpu-shadertoy = "wgpu_shadertoy.cli:main_cli" | ||
|
||
[project.urls] | ||
Repository = "https://github.com/pygfx/shadertoy" | ||
|
||
|
@@ -52,5 +58,5 @@ extend-ignore = [ | |
"E501", # line too long | ||
] | ||
|
||
[tool.ruff.per-file-ignores] | ||
[tool.ruff.lint.per-file-ignores] | ||
"__init__.py" = ["F401"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import pytest | ||
from testutils import can_use_wgpu_lib | ||
|
||
from wgpu_shadertoy.api import _get_api_key, shader_args_from_json, shadertoy_from_id | ||
|
||
if not can_use_wgpu_lib: | ||
pytest.skip("Skipping tests that need the wgpu lib", allow_module_level=True) | ||
|
||
|
||
@pytest.fixture | ||
def api_available(): | ||
""" | ||
Skip tests some tests if no API is unavailable. | ||
""" | ||
try: | ||
return _get_api_key() | ||
except Exception as e: | ||
pytest.skip("Skipping API tests: " + str(e)) | ||
|
||
|
||
# coverage for shadertoy_from_id(id_or_url) | ||
def test_from_id_with_invalid_id(api_available): | ||
with pytest.raises(RuntimeError): | ||
shadertoy_from_id("invalid_id") | ||
|
||
|
||
def test_from_id_with_valid_id(api_available): | ||
# shadertoy source: https://www.shadertoy.com/view/mtyGWy by kishimisu | ||
data = shadertoy_from_id("mtyGWy") | ||
assert "Shader" in data | ||
assert data["Shader"]["info"]["id"] == "mtyGWy" | ||
assert data["Shader"]["info"]["username"] == "kishimisu" | ||
|
||
|
||
def test_shadertoy_from_id(api_available): | ||
# Import here, because it imports the wgpu.gui.auto | ||
from wgpu_shadertoy import Shadertoy | ||
|
||
# shadertoy source: https://www.shadertoy.com/view/l3fXWN by Vipitis | ||
shader = Shadertoy.from_id("l3fXWN") | ||
|
||
assert shader.title == "API test for CI by jakel101" | ||
assert shader.shader_type == "glsl" | ||
assert shader.shader_code.startswith("//Confirm API working!") | ||
assert shader.common.startswith("//Common pass loaded!") | ||
assert shader.inputs[0].sampler_settings["address_mode_u"] == "clamp-to-edge" | ||
assert shader.inputs[0].data.shape == (32, 256, 4) | ||
assert shader.inputs[0].texture_size == (256, 32, 1) | ||
|
||
|
||
# coverage for shader_args_from_json(dict_or_path, **kwargs) | ||
def test_from_json_with_invalid_path(): | ||
with pytest.raises(FileNotFoundError): | ||
shader_args_from_json("/invalid/path") | ||
|
||
|
||
def test_from_json_with_invalid_type(): | ||
with pytest.raises(TypeError): | ||
shader_args_from_json(123) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
from .shadertoy import Shadertoy, ShadertoyChannel | ||
from .inputs import ShadertoyChannel | ||
from .shadertoy import Shadertoy | ||
|
||
__version__ = "0.1.0" | ||
version_info = tuple(map(int, __version__.split("."))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import json | ||
import os | ||
|
||
import numpy as np | ||
import requests | ||
from PIL import Image | ||
from wgpu import logger | ||
|
||
from .inputs import ShadertoyChannel | ||
|
||
HEADERS = {"user-agent": "https://github.com/pygfx/shadertoy script"} | ||
|
||
|
||
def _get_api_key(): | ||
key = os.environ.get("SHADERTOY_KEY", None) | ||
if key is None: | ||
raise ValueError( | ||
"SHADERTOY_KEY environment variable not set, please set it to your Shadertoy API key to use API features. Follow the instructions at https://www.shadertoy.com/howto#q2" | ||
) | ||
test_url = "https://www.shadertoy.com/api/v1/shaders/query/test" | ||
test_response = requests.get(test_url, params={"key": key}, headers=HEADERS) | ||
if test_response.status_code != 200: | ||
raise requests.exceptions.HTTPError( | ||
f"Failed to use ShaderToy API with key: {test_response.status_code}" | ||
) | ||
test_response = test_response.json() | ||
if "Error" in test_response: | ||
raise ValueError( | ||
f"Failed to use ShaderToy API with key: {test_response['Error']}" | ||
) | ||
return key | ||
|
||
|
||
def _download_media_channels(inputs): | ||
""" | ||
Downloads media (currently just textures) from Shadertoy.com and returns a list of `ShadertoyChannel` to be directly used for `inputs`. | ||
Requiers internet connection (API key not required). | ||
""" | ||
media_url = "https://www.shadertoy.com" | ||
channels = {} | ||
for inp in inputs: | ||
if inp["ctype"] != "texture": | ||
continue # TODO: support other media types | ||
response = requests.get(media_url + inp["src"], headers=HEADERS, stream=True) | ||
if response.status_code != 200: | ||
raise requests.exceptions.HTTPError( | ||
f"Failed to load media {media_url + inp['src']} with status code {response.status_code}" | ||
) | ||
img = Image.open(response.raw).convert("RGBA") | ||
img_data = np.array(img) | ||
channel = ShadertoyChannel( | ||
img_data, kind="texture", wrap=inp["sampler"]["wrap"] | ||
) | ||
channels[inp["channel"]] = channel | ||
return list(channels.values()) | ||
|
||
|
||
def _save_json(data, path): | ||
with open(path, "w", encoding="utf-8") as f: | ||
json.dump(data, f, indent=2) | ||
|
||
|
||
def _load_json(path) -> dict: | ||
with open(path, "r", encoding="utf-8") as f: | ||
return json.load(f) | ||
|
||
|
||
def shadertoy_from_id(id_or_url) -> dict: | ||
""" | ||
Fetches a shader from Shadertoy.com by its ID (or url) and returns the JSON data as dict. | ||
""" | ||
if "/" in id_or_url: | ||
shader_id = id_or_url.rstrip("/").split("/")[-1] | ||
else: | ||
shader_id = id_or_url | ||
url = f"https://www.shadertoy.com/api/v1/shaders/{shader_id}" | ||
response = requests.get(url, params={"key": _get_api_key()}, headers=HEADERS) | ||
if response.status_code != 200: | ||
raise requests.exceptions.HTTPError( | ||
f"Failed to load shader at https://www.shadertoy.com/view/{shader_id} with status code {response.status_code}" | ||
) | ||
shader_data = response.json() | ||
if "Error" in shader_data: | ||
raise RuntimeError( | ||
f"Shadertoy API error: {shader_data['Error']} for https://www.shadertoy.com/view/{shader_id}, perhaps the shader isn't set to `public+api`" | ||
) | ||
return shader_data | ||
|
||
|
||
def shader_args_from_json(dict_or_path, **kwargs) -> dict: | ||
""" | ||
Builds the args for a `Shadertoy` instance from a JSON-like dict of Shadertoy.com shader data. | ||
""" | ||
if isinstance(dict_or_path, (str, os.PathLike)): | ||
shader_data = _load_json(dict_or_path) | ||
else: | ||
shader_data = dict_or_path | ||
|
||
if not isinstance(shader_data, dict): | ||
raise TypeError("shader_data must be a dict") | ||
main_image_code = "" | ||
common_code = "" | ||
inputs = [] | ||
if "Shader" not in shader_data: | ||
raise ValueError( | ||
"shader_data must have a 'Shader' key, following Shadertoy export format." | ||
) | ||
for r_pass in shader_data["Shader"]["renderpass"]: | ||
if r_pass["type"] == "image": | ||
main_image_code = r_pass["code"] | ||
if r_pass["inputs"] is not []: | ||
inputs = _download_media_channels(r_pass["inputs"]) | ||
elif r_pass["type"] == "common": | ||
common_code = r_pass["code"] | ||
else: | ||
# TODO should be a warning and not verbose! | ||
logger.warn( | ||
f"renderpass of type {r_pass['type']} not yet supported, will be omitted." | ||
) | ||
title = f'{shader_data["Shader"]["info"]["name"]} by {shader_data["Shader"]["info"]["username"]}' | ||
|
||
shader_args = { | ||
"shader_code": main_image_code, | ||
"common": common_code, | ||
"shader_type": "glsl", | ||
"inputs": inputs, | ||
"title": title, | ||
**kwargs, | ||
} | ||
return shader_args |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import argparse | ||
|
||
from .shadertoy import Shadertoy | ||
|
||
argument_parser = argparse.ArgumentParser( | ||
description="Download and render Shadertoy shaders" | ||
) | ||
|
||
argument_parser.add_argument( | ||
"shader_id", type=str, help="The ID of the shader to download and render" | ||
) | ||
argument_parser.add_argument( | ||
"--resolution", | ||
type=int, | ||
nargs=2, | ||
help="The resolution to render the shader at", | ||
default=(800, 450), | ||
) | ||
|
||
|
||
def main_cli(): | ||
args = argument_parser.parse_args() | ||
shader_id = args.shader_id | ||
resolution = args.resolution | ||
shader = Shadertoy.from_id(shader_id, resolution=resolution) | ||
shader.show() | ||
|
||
|
||
if __name__ == "__main__": | ||
main_cli() |
Oops, something went wrong.